Hi,
this looks like a very good question - unless I'm missing something, there is no function in the F# library that would allow you to do this. I'll pass this around to the F# team as a feedback, because I think that function like this should definitely be there.

In the meantime, you can use the following workaround - functions are represented as FastFunc .NET type, which has an "Invoke" method, so you can write this (it's a bit ugly though):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
  let foo1 x = x + 1
  let foo2 x y = x + y
  let foo3 (x, y) = x + y

  let funcType = typedefof<FastFunc<obj, obj>>
  
  let invoke (f:obj) (args:list<obj>) =
    let ty = f.GetType()
    // Find the 'FastFunc' type of the object
    let rec findFuncType (t:System.Type) = 
      if (t = typeof<obj>) then failwith "Invalid argument 'f'!"
      if (t.IsGenericType && funcType = t.GetGenericTypeDefinition()) then t 
      else findFuncType t.BaseType

    // Invoke the function:
    // It is compiled as FastFunc<int, int> or FastFunc<int, FastFunc<int, int>>, 
    // so we call its 'Invoke' method recursively until we get the result.
    args |> List.fold_left (fun f arg -> 
      let func = findFuncType (f.GetType())
      func.GetMethod("Invoke").Invoke(f, [| arg |])) f
    
  printfn "CALLING"  
  invoke (box foo1) [ box 41 ] |> printfn "%A"
  invoke (box foo2) [ box 20; box 22 ] |> printfn "%A"
  invoke (box foo3) [ box (20, 22) ] |> printfn "%A"
By on 11/18/2008 3:04 AM ()

Hi Tomasp,

Thanks for the prompt reply!

I was just at the point where I got something similar working, but it looks a little bit like black magic (that is, fragile):

1
2
3
4
5
6
7
8
let Invoke (f : obj) (args : obj array) = 

    f.GetType().InvokeMember("Invoke", System.Reflection.BindingFlags.InvokeMethod, null, f, args)

Invoke (box (fun x y z -> x + y + z)) [| box 1; box 2; box 3|]  // = 6

Invoke (box (fun x y   -> x - y))     [| box 1; box 2; |]       // = -1

At first I thought this is the same as yours modulo run-time type-checking, but your code dynamically "Invokes" arguments one-by-one, right? While my code throws them at once. I'm very surprised it works - is it something to be relied on?

Thanks again.

--Anton

By on 11/18/2008 3:22 AM ()

Yes, the difference is that my version invoked the function arguments one by one.

It seems that your version is better - for closures, the compiler also generates "Invoke" method that can be used directly. My version should work unless the FastFunc type changes, so it may be more stable, but in practice I haven't found a case where your code wouldn't work (plus I guess it will be more efficient)...

I was thinking that the compiler may not generate a type with "Invoke" here:

1
2
3
 
let foo a b = a + b;;
Invoke (box foo) [| box 1; box 2 |];;

but it still does that... so I guess your version should work fine!
Anyway, this is really something that should be handled in the F# libraries....

By on 11/18/2008 3:50 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper