无法从 C# 调用部分应用的 F# 函数作为方法
Cannot invoke partially applied F# function as a method from C#
我在 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);
原则上,去除参数和使用偏函数应用给你相同的功能,但有细微的差别。明显的一个是值限制(但这里不适用)。更微妙的是函数是如何编译的。
您实际上可以看到这一点,即使在 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# 使用,那么我会简单地避免在 public API(以及其他特定于 F# 的类型)中使用部分函数应用程序).
我在 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);
原则上,去除参数和使用偏函数应用给你相同的功能,但有细微的差别。明显的一个是值限制(但这里不适用)。更微妙的是函数是如何编译的。
您实际上可以看到这一点,即使在 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# 使用,那么我会简单地避免在 public API(以及其他特定于 F# 的类型)中使用部分函数应用程序).