将F#func转换为表达式<Func<>>



我有一个带有以下签名的函数的模块:

module Something =
let someFunc func = // ('TType -> 'TField) -> 'TValue
...

在该函数内部,我从某个外部库中调用一个函数,该函数具有以下签名(C#(的方法:

class SomeClass
{
public ReturnType<TType> SomeMethod<TField>(func: Expression<Func<TType, TField>>) { ... }
}

当我试图传递一个'TType -> 'TField函数时,我得到一个错误,它不能转换为Expression<Func<'TType, 'TField>>。我在StackOverflow上发现了以下问题:问题

但它并不能解决我的问题(第一个答案不起作用,第二个答案起作用,但我必须更改函数的签名(。

对于第二个答案,我必须将函数的签名更改为以下内容:

module Something =
let someFunc func = // Expression<Func<'TType, 'TField>>) -> 'TValue
...

添加对我的模块的"客户端"可见的附加类,如下所示:

type ExpressionHelper() =
static member AsExpression<'TType, 'TField>(e: Expression<Func<'TType, 'TField>>) = e

所以最后的调用看起来是这样的:

let _ = Something.someFunc (fun (o: SomeType) -> o.someField)

看起来是这样的:

let _ = Something.someFunc (ExpressionHelper.AsExpression (fun (o: SomeType) -> o.SomeField))

我不想强迫我的模块的user显式地将F#函数转换为Expression<Func<'TType, 'TField>>。我想在我的模块中做到这一点,有什么方法可以实现吗?

如果您有'T1 -> 'T2类型的值,则无法将其转换为Expression<Func<'T1, 'T2>>类型的值。这是不可能的,因为前者是一个已编译的函数(引用某个对象及其方法的委托(,而后者是原始源代码的表示。

因此,您需要使用Expression<...>作为参数的类型来实现这一点(或者Expr,如果要使用引号,则相当于F#(。

然而,在F#中,编译器会自动将使用lambda函数语法fun x -> ..创建的值转换为Expression<...>类型的值。它不对let绑定函数的参数执行此操作,但对静态方法的参数执行该操作。这意味着您可以使用:

open System
open System.Linq.Expressions
type A = 
static member foo (f:Expression<Func<int, int>>) = 
f.ToString()
A.foo (fun n -> n + 1)

最新更新