将 F# func 转换为 Expression<Func<..,..>>

MNi*_*Nie 3 lambda f# casting c#-to-f#

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

module Something =
    let someFunc func = // ('TType -> 'TField) -> 'TValue
        ...
Run Code Online (Sandbox Code Playgroud)

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

class SomeClass
{
    public ReturnType<TType> SomeMethod<TField>(func: Expression<Func<TType, TField>>) { ... }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试在'TType -> 'TField那里传递一个函数时,我收到一个错误,提示它不能转换为Expression<Func<'TType, 'TField>>. 我在 StackOverflow 上发现了以下问题:question

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

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

module Something =
    let someFunc func = // Expression<Func<'TType, 'TField>>) -> 'TValue
        ...
Run Code Online (Sandbox Code Playgroud)

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

type ExpressionHelper() =
    static member AsExpression<'TType, 'TField>(e: Expression<Func<'TType, 'TField>>) = e
Run Code Online (Sandbox Code Playgroud)

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

let _ = Something.someFunc (fun (o: SomeType) -> o.someField)
Run Code Online (Sandbox Code Playgroud)

看起来像这样:

let _ = Something.someFunc (ExpressionHelper.AsExpression (fun (o: SomeType) -> o.SomeField))
Run Code Online (Sandbox Code Playgroud)

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

Tom*_*cek 5

如果您有一个 type 值'T1 -> 'T2,则无法将其转换为 type 值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)
Run Code Online (Sandbox Code Playgroud)