假设我有一些像
这样的库代码open System
module Foo =
let Invoke1 (action : Func<string>) = ()
let Invoke2 (action : Func<int, string>) = ()
let Invoke3 (key : int, add : Func<int, string>, update: Func<int, string, string>) = ()
let Invoke4 (key : int) (add : Func<int, string>) (update: Func<int, string, string>) = ()
type Bar =
static member Invoke1 (action : Func<string>) = ()
static member Invoke2 (action : Func<int, string>) = ()
static member Invoke3 (key : int, add : Func<int, string>, update: Func<int, string, string>) = ()
static member Invoke4 (key : int) (add : Func<int, string>) (update: Func<int, string, string>) = ()
现在,当调用这些方法时,实际上存在行为差异
首先,以下三行不编译
Foo.Invoke1(fun () -> "")
Foo.Invoke2(fun a -> "")
Foo.Invoke3(5, (fun k -> ""), (fun k v -> v))
它们都有相同的编译错误
error FS0002: This function takes too many arguments, or is used in a context where a function is not expected
但是,以下三行编译得很好
Bar.Invoke1(fun () -> "")
Bar.Invoke2(fun a -> "")
Bar.Invoke3(5, (fun k -> ""), (fun k v -> v))
因此,当某个类型的静态成员接受System.Func时,可以隐式转换和接受相应的F#lambda。但是为了让模块绑定,它不起作用?
我还使用ILSpy来查看生成的IL。对于Foo.Invoke1和Bar.Invoke1,它们在IL中具有与
相同的签名.method public static
string Invoke1 (
class [mscorlib]System.Func`1<string> action
) cil managed
因此IL中的方法本身没有区别。对于那种类型,我看到了
.class public auto ansi abstract sealed Library.Foo
.class public auto ansi serializable Library.Bar
所以这会以某种方式造成差异?无论如何,如何解释模块和类型之间的行为差异?
然后我还发现以下内容无法编译
Bar.Invoke4 (5) (fun k -> "") (fun k v -> v)
出现错误
error FS0001: This expression was expected to have type int but here has type unit
Bar.Invoke3和Bar.Invoke4之间的区别在于前者使用元组形式而后者使用currying形式。但是,不知何故,后者不编译。万一你好奇
Foo.Invoke4 (5) (fun k -> "") (fun k v -> v)
也不编译,只是错误不同,它与所有其他“Foo”错误相同:
error FS0002: This function takes too many arguments, or is used in a context where a function is not expected
有关为什么Bar.Invoke3有效,但Bar.Invoke4没有的想法
当从F#lambda到System.Func的隐式转换发生并且不可能发生时,真的很混乱。对上述行为有什么解释?
我发现了一些早先的相关问题,例如
但仍无法找到行为的明确解释。
有什么想法吗?
注意:我用F#4.0尝试了上面的代码
某些上下文:当我试图探索是否可以编写F#库时,我发现了这样的行为,其中一些方法将functor作为参数。我能否以可以从F#/ C#代码中使用它的方式编写它。我记得我可以使用带有System.Func作为参数的.Net方法的F#lambda(例如ConcurrentDictonary的AddOrUpdate)。所以我想如果我的函数使用System.Func,它可以同时服务。 这是一个好/坏的假设?
答案 0 :(得分:4)
在F#中,类型导向的转换仅适用于类型成员的调用(F#spec 8.13.7)