代码之家  ›  专栏  ›  技术社区  ›  Muhammad Alkarouri

F#动态操作符,提供对函数和函数名的访问

  •  0
  • Muhammad Alkarouri  · 技术社区  · 16 年前

    给定一系列函数 test1 test2

    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 回复  |  直到 16 年前
        1
  •  1
  •   Brian    16 年前

    下面是一些示例代码,展示了其中的一些内容。我用 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()
    
        2
  •  3
  •   Tomas Petricek    16 年前

    您不能使用 ? 运算符访问模块中的函数,因为 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”更好一点,但我希望这能证明这一点。不幸的是,这不能用于模块。