使用f# lambda作为期望System.Func的形式形参的实际实参时,模块与类型的行为差异



假设我有一些像

这样的库代码
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。函数,相应的f# lambda可以隐式转换和接受。但是对于模块的let绑定,它不起作用?

我还使用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是前者使用元组形式,后者使用curry形式。然而,不知何故,后者不能编译。如果你很好奇

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

知道为什么吗?Invoke3工作,但Bar。Invoke4没有?

当从f# lambda隐式转换到System。什么时候会发生,什么时候不会发生。对上述行为的解释是什么?

我发现了一些早前的相关问题,比如

  • 传递f#函数给IEnumerable。Where vs IEnumerable。所有的
  • lambda表达式到Func的转换

但仍然找不到一个明确的解释。

任何想法?

注意:我用f# 4.0试过上面的代码

一些上下文:当我试图探索是否可以编写一个f#库,其中一些方法以函子作为参数时,我发现了这样的行为。我能不能把它写成一种可以在f#/c#代码中使用的方式?我记得我可以在。net方法中使用f# lambda。函数作为参数(例如concurrentdictionary的AddOrUpdate)。所以我想如果我的函数使用System。Func,它可以服务于两者。这是一个好/坏的假设?

在f#中,类型定向转换只应用于类型成员的调用(f# spec 8.13.7)

最新更新