使用F#lambda作为期望System.Func的形式参数的实际参数时模块与类型行为的差异 [英] module vs type behavior difference on using F# lambda as actual argument to formal parameter that expects System.Func

查看:101
本文介绍了使用F#lambda作为期望System.Func的形式参数的实际参数时模块与类型行为的差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一些类似的库代码

Assume I have some library code like

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

现在调用这些方法时,实际上存在行为差异

Now when these methods were invoked, there're actually behavior difference

首先,以下三行不编译

Foo.Invoke1(fun () -> "") 
Foo.Invoke2(fun a -> "") 
Foo.Invoke3(5, (fun k -> ""), (fun k v -> v))

它们都具有相同的编译错误

They all have the same compilation error

error FS0002: This function takes too many arguments, or is used in a context where a function is not expected

但是,以下三行编译就可以了

However, the following three lines compile just fine

Bar.Invoke1(fun () -> "") 
Bar.Invoke2(fun a -> "")
Bar.Invoke3(5, (fun k -> ""), (fun k v -> v)) 

以某种方式,当类型的静态成员接受System.Func时,可以隐式转换和接受相应的F#lambda.但是对于模块的let绑定,它不起作用吗?

So somehow when static member of a type accepts System.Func, a corresponding F# lambda can be implicitly converted and accepted. But for let bindings of module, it does not work?

我还使用ILSpy查看生成的IL.对于Foo.Invoke1和Bar.Invoke1,它们在IL中的签名与

I also used ILSpy to look at the IL generated. For Foo.Invoke1 and Bar.Invoke1, they have the same signature in IL as

.method public static 
  string Invoke1 (
    class [mscorlib]System.Func`1<string> action
  ) cil managed 

因此IL的方法本身没有区别.对于类型,我看到了

So there's no difference on the method itself in IL. For the type, I saw

.class public auto ansi abstract sealed Library.Foo
.class public auto ansi serializable Library.Bar

所以这会导致差异吗?无论如何,如何解释模块和类型之间的行为差​​异?

So this somehow causes the difference? Anyway, how to explain the behavior difference between module and type?

然后我还发现以下内容无法编译

Then I also found that the following does not compile

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之间的区别在于,前者使用元组形式,而后者使用柯林形式.但是,后者无法编译.万一你好奇

The difference between Bar.Invoke3 and Bar.Invoke4 is that the former uses tuple form and the latter uses currying form. However, somehow the latter does not compile. In case you are curious

Foo.Invoke4 (5) (fun k -> "") (fun k v -> v)

也不编译,只是错误有所不同,与所有其他"Foo"错误相同:

does not compile either, it's just the error is different, it's the same as all other "Foo" errors:

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时以及何时无法发生时,这确实令人困惑.对上述行为的解释是什么?

It's really confusing that when implicit conversion from F# lambda to System.Func can happen and when it cannot happen. What are the explanations for the behaviors described above?

我找到了一些较早的相关问题,例如

I found some earlier related questions, such as

  • Passing F# function to IEnumerable.Where vs IEnumerable.All
  • Conversion of lambda expressions to Func

但是仍然找不到关于这些行为的明确解释.

But still could not find a clear explanation on the behaviors.

有什么主意吗?

注意:我在F#4.0中尝试了上面的代码

Note: I tried the above code with F# 4.0

某些上下文:当我尝试探索是否可以编写F#库时发现了这样的行为,其中某些方法将仿函数作为参数.我能以一种可以同时在F#/C#代码中使用的方式编写它吗?我记得我可以将F#lambda与以System.Func作为参数的.Net方法一起使用(例如ConcurrentDictonary的AddOrUpdate).因此,我认为如果我的功能使用的是System.Func,则可以同时使用两者. 这是一个好/坏的假设吗?

Some context: I found such behaviors when I was trying to explore whether I can write a F# library where some methods takes functors as parameter. Can I write it in a way that it can be used from both F#/C# code. I remember I can use F# lambda with .Net methods that takes System.Func as parameter (e.g. ConcurrentDictonary's AddOrUpdate). So I thought if my functions are using System.Func, it can serve both. Is this a good/bad assumption?

推荐答案

在F#中,类型导向的转换仅适用于类型成员的调用(F#规范8.13.7)

In F# type-directed conversions are applied only in invocations of type members (F# spec 8.13.7)

这篇关于使用F#lambda作为期望System.Func的形式参数的实际参数时模块与类型行为的差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆