F#动态运算符,提供对函数和函数名称的访问

时间:2010-07-05 10:41:13

标签: dynamic f#

给定一些函数test1test2,...属于一个模块:

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

如何使用(?)运算符来访问函数名和函数本身?结果应如下所示:

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

2 个答案:

答案 0 :(得分:3)

您不能使用?运算符来访问模块中的函数,因为构造Checks?test1在语法上不正确(这将转换为(?) Checks "test"并且您不能使用模块名称作为值)。

但是,应该可以使用对象的实例(例如obj?test)对类型的成员执行此操作。或者,您可以编写一个“假”对象实例(它知道模块的名称)。然后,?的实现将查找模块并搜索模块中的静态成员。

最简单的实现(第一种情况)看起来像这样:

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)

你也可以添加一些包装来使函数'f'更好一些,但我希望这能证明这个想法。不幸的是,这不适用于模块。

答案 1 :(得分:1)

这里有一些示例代码,展示了其中的一些内容。我使用D作为Checks模块加上函数名称的“动态”访问。

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()