tags:

views:

97

answers:

2

Given a number of functions test1, test2, ... belonging to a module:

module Checks =
    let test1 x = ...
    let test2 x = ...
    ...

how can the (?) operator be used to give access to both the function name and the function itself? The result should look like:

let name, func = Checks?test1
assert(name = "test1")
assert(func(x) = Checks.test1(x)) //whatever x is (test1 is known to be pure)
+3  A: 

You cannot use the ? operator to access functions in a module, because the construct Checks?test1 is not syntactically correct (this would be translated to (?) Checks "test" and you cannot use module names as values).

However, it should be possible to do this for members of a type using an instance of the object (e.g. obj?test). Alternatively you could write a "fake" object instance (that knows the name of the module). The implementation of ? would then look for the module and search static members in the module.

The simplest implementation (of the first case) would look like this:

let (?) obj s = 
  let memb = obj.GetType().GetMethod(s)
  // Return name and a function that runs the method
  s, (fun args -> memb.Invoke(obj, args))

// Type that contains tests as members    
type Check() = 
  member x.test1 () = 32

// We need to create instance in order to use '?'
let ch = Check()
let s,f = ch?test1

// Function 'f' takes array of objects as an argument and
// returns object, so the call is not as elegant as it could be
let n = ((f [| |]) :?> int)

You could also add some wrapping to make the function 'f' a little bit nicer, but I hope this demonstrates the idea. Unfortunately, this cannot work for modules.

Tomas Petricek
+1  A: 

Here's some sample code that shows off some of this. I use D as the 'dynamic' access of the Checks module plus function name.

module Checks = 
    let test1(x) = printfn "test1 %d" x
    let test2(x,y) = printfn "test2 %s %d" x y

type MyDynamic() = class end
let D = new MyDynamic()
let (?) (md:MyDynamic) fname : (string * ('a -> 'r)) =
    let a = md.GetType().Assembly
    let t = a.GetType("Program+Checks")
    let m = t.GetMethod(fname)
    let f arg = 
        let at = arg.GetType()
        let fsharpArgs = 
            if at.IsGenericType && at.GetGenericTypeDefinition().FullName.StartsWith("System.Tuple`") then
                Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(arg)
            else
                [| box arg |]
        unbox(m.Invoke(null, fsharpArgs))
    fname, f

let Main() =
    let x = 42
    let s = "foo"
    let name, func = D?test1 
    assert(name = "test1") 
    assert(func(x) = Checks.test1(x))

    let name, func = D?test2
    assert(name = "test2") 
    assert(func(s,x) = Checks.test2(s,x))

    System.Console.ReadKey()

Main()
Brian