无法从C#中将部分应用的F#函数作为方法调用

4

我在F#中编写了一个简单的函数,并能够正确地从同一解决方案中另一个项目中的C#代码调用它。然后,我尝试进行重构,使得我从C#中调用的F#函数是原始函数部分应用的结果,但出现了编译时错误Non-invocable member: 'Calculators.TenPercentCalculator' cannot be used like a method.

这里是工作的F#代码:

    let TenPercentItemCalculator (paymentItem : PaymentItem) = 
        1.1M * paymentItem.Amount

    let ExtractItems (payment : Payment) = List.ofSeq(payment.PaymentItems) 

    let TenPercentCalculator  (payments : Payment seq) =
        payments |> Seq.map ExtractItems |> List.concat |> List.map TenPercentItemCalculator |> List.sum

下面是重构后的 F# 代码,但该代码无法编译:

    let TenPercentItemCalculator (paymentItem : PaymentItem) = 
        1.1M * paymentItem.Amount

    let ExtractItems (payment : Payment) = List.ofSeq(payment.PaymentItems) 

    let BaseCalculator (itemCalculator: PaymentItem -> decimal)  (payments : Payment seq) =
        payments |> Seq.map ExtractItems |> List.concat |> List.map itemCalculator |> List.sum

    let TenPercentCalculator : (Payment seq -> decimal) = BaseCalculator TenPercentItemCalculator

在这两种情况下,以下是调用 TenPercentCalculator 函数的 C# 代码:

var interest = Calculators.TenPercentAuctionCalculator(SampleLedger.Payments);

1
部分应用函数在C#中被视为类型为Microsoft.FSharp.Core.FSharpFunc<'T,'U>的对象。我对此并不熟悉,但我猜想您需要显式调用Invoke,例如...tionCalculator.Invoke(SampleLedg...)。请参见此处(搜索“Functions”):http://www.voyce.com/index.php/2011/05/09/mixing-it-up-when-f-meets-c/。 - Søren Debois
1个回答

6

原则上,删除参数并使用部分函数应用会给你相同的函数,但有细微差别。显然的是值限制(但这不适用于此处);更微妙的是函数的编译方式。

甚至在F#类型签名中也可以看到这一点。考虑:

let normal nums = Array.map ((+) 1) nums
let partial = Array.map ((+) 1)

两者的类型如下所示:
val normal : int [] -> int []
val partial : (int [] -> int [])

如您所见,当函数部分应用时,编译器会打印出额外的括号。大多数情况下,您可以忽略这一点,但这意味着该函数将被编译为返回FSharpFunc<...>的属性,而不是普通的.NET方法。

如果您还添加到FSharp.Core的引用,则仍然可以调用它:

Foo.normal(Enumerable.Range(1, 10).ToArray());
Foo.partial.Invoke(Enumerable.Range(1, 10).ToArray());

但是,如果你正在设计一个应该易于从C#使用的库,那么我建议在公共API中避免使用部分函数应用(以及其他F#特定类型)。


谢谢,Tomas。我将那个部分应用的函数重写为 let TenPercentCalculator (payments : Payment seq) = BaseCalculator TenPercentItemCalculator payments,它可以正常工作——我认为在这种情况下这是一个安全的重构? - Larry Lustig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接