Конвертировать F # fun c в Expression > - PullRequest
2 голосов
/ 21 марта 2020

У меня есть модуль с функцией со следующей сигнатурой:

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>>. Я хочу сделать это внутри моего модуля, есть ли какой-нибудь способ достичь этого?

1 Ответ

3 голосов
/ 23 марта 2020

Если у вас есть значение типа 'T1 -> 'T2, вы не сможете превратить его в значение типа Expression<Func<'T1, 'T2>>. Это невозможно, поскольку первая представляет собой скомпилированную функцию (делегат, ссылающийся на некоторый объект и его метод), а вторая представляет собой представление исходного исходного кода.

Итак, вам нужно будет использовать Expression<...> в качестве типа аргумента для этой работы (или Expr, что эквивалентно F #, если вы должны использовать цитаты).

Однако в F # есть случаи, когда компилятор автоматически поворачивает значение, созданное с использованием синтаксиса лямбда-функции fun x -> .., в значение типа Expression<...>. Он не делает этого для аргументов функций с привязкой, но делает это для аргументов методов stati c. Это означает, что вы можете использовать:

open System
open System.Linq.Expressions

type A = 
  static member foo (f:Expression<Func<int, int>>) = 
    f.ToString()

A.foo (fun n -> n + 1)
...