yuyoyuppe
yuyoyuppe

Reputation: 1602

F# and lisp-like apply function

For starters, I'm a novice in functional programming and F#, therefore I don't know if it's possible to do such thing at all. So let's say we have this function:

let sum x y z = x + y + z

And for some reason, we want to invoke it using the elements from a list as an arguments. My first attempt was just to do it like this:

//Seq.fold (fun f arg -> f arg) sum [1;2;3] 

let rec apply f args =
    match args with
    | h::hs -> apply (f h) hs
    | [] -> f

...which doesn't compile. It seems impossible to determine type of the f with a static type system.
There's identical question for Haskell and the only solution uses Data.Dynamic to outfox the type system. I think the closest analog to it in F# is Dynamitey, but I'm not sure if it fits. This code

let dynsum = Dynamitey.Dynamic.Curry(sum, System.Nullable<int>(3))

produces dynsum variable of type obj, and objects of this type cannot be invoked, furthermore sum is not a .NET Delegate.

So the question is, how can this be done with/without that library in F#?

Upvotes: 1

Views: 327

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243061

F# is a statically typed functional language and so the programming patterns that you use with F# are quite different than those that you'd use in LISP (and actually, they are also different from those you'd use in Haskell). So, working with functions in the way you suggested is not something that you'd do in normal F# programming.

If you had some scenario in mind for this function, then perhaps try asking about the original problem and someone will help you find an idiomatic F# approach!

That said, even though this is not recommended, you can implement the apply function using the powerful .NET reflection capabilities. This is slow and unsafe, but if is occasionally useful.

open Microsoft.FSharp.Reflection

let rec apply (f:obj) (args:obj list) =
  let invokeFunc = 
    f.GetType().GetMethods()
    |> Seq.find (fun m -> 
        m.Name = "Invoke" && 
        m.GetParameters().Length = args.Length)
  invokeFunc.Invoke(f, Array.ofSeq args)  

The code looks at the runtime type of the function, finds Invoke method and calls it.

let sum x y z = x + y + z
let res = apply sum [1;2;3]
let resNum = int res

At the end, you need to convert the result to an int because this is not statically known.

Upvotes: 3

Related Questions