I am encountering this Function
type that I need to pass to a JQueryAnimationOptions
object. I usually would pass a lambda to callbacks but these seem to be incompatible. I looked up every sample I could find in the FunScript repo. and couldn't find any workaround.
It also said the the Function
is actually an interface (for what?) when used as a return statement Error: Invalid use of interface type
.
So how to pass a callback argument with this Function
type?
the code:
[<FunScript.JS>]
module Main
open FunScript
open FunScript.TypeScript
let sayHelloFrom (name:string) =
Globals.window.alert("Hello, " + name)
let jQuery (selector:string) = Globals.jQuery.Invoke selector
let main() =
let options = createEmpty<JQueryAnimationOptions>()
options.duration <- 3000
options.complete <- (fun _ -> sayHelloFrom("F#"))
let properties = createEmpty<Object>()
properties.Item("opacity") <- 1
let mainContent = jQuery "#mainContent"
mainContent.animate(properties, options) |> ignore
mainContent.click(fun e -> sayHelloFrom("F#") :> obj)
This works more or less as you would expect when passing lambdas between F# and C#. In F#, functions can be curried, while in C# (and JavaScript) cannot. So when you need to send a lambda from F# to C# you need to convert it first. In F# this is done by wrapping the lambda like this:
open System.Linq
open System.Collections.Generic
let ar = [|1;2;3|]
let f = fun (x: int) (y: int) -> x + y
let acc = ar.Aggregate( System.Func<int,int,int>(f) )
Actually, the F# compiler can deduce the types most of the times, so you only need to write: System.Func<_,_,_>(f)
. Furthermore, when passing a F# lambda to a method expecting a C# lambda, the compiler makes the wrapping automatically for you. Then the previous example becomes:
let ar = [|1;2;3|]
let acc = ar.Aggregate( fun x y -> x + y )
(Of course, in this case it would be better to use the idiomatic Array.reduce. This is just a contrived example.)
This works exactly the same when interacting with JS using FunScript. The only thing you need to be aware of is how F# lambdas get translated to JS. To allow currying, a lambda with two or more parameters like fun x y -> x + y
becomes:
function (x) {
return function (y) {
return x + y;
}
}
Which may be a problem because the native JS will expect the following signature: function (x, y)
. In that case, you would have to wrap the lambda with System.Func<_,_,_>()
as when interacting with C# (remember this is done automatically if you pass the lambda to a method).
However, lambdas with just one parameter don't suppose any problem: fun x -> x*x
becomes function (x) { return x*x; }
. In that case you don't need to wrap them (it doesn't hurt to do it anyway) and it's enough just to use unbox
to appease the F# compiler when necessary. Just please be aware the FunScript compiler ignores unbox
in the final JS code so there'll be no type check at all at runtime.
I hope the explanation is clear. Please add a comment if it isn't and I'll edit the answer.