MNie
MNie

Reputation: 1367

Convert F# func to Expression<Func<..,..>>

I have a module with a function with the following signature:

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

and inside that function I invoke a function from some external library which has a method with following signature (C#):

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

When I try to pass a 'TType -> 'TField function there I get an error that it isn't convertible to Expression<Func<'TType, 'TField>>. I found the following question on StackOverflow: question

But it doesn't resolve my issue (The First answer didn't work, the second one works, but I have to change the signature of my function).

With the second answer I have to change the signature of my function to the following:

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

Add additional class visible for a "client" of my module, which looks like this:

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

So the final invocation instead looking like this:

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

looks like that:

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

I don't wanna force the user of my module to convert F# function to Expression<Func<'TType, 'TField>> explicitly. I wanna do that inside my module, is there some way to achieve that?

Upvotes: 6

Views: 363

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243051

If you have a value of type 'T1 -> 'T2, there is no way you can turn it into a value of type Expression<Func<'T1, 'T2>>. This is not possible, because the former is a compiled function (a delegate referring to some object and its method), while the latter is a representation of the original source code.

So, you will need to use Expression<...> as the type of the argument to make this work (or Expr, which is the F# equivalent if you were to use quotations).

However, there are cases in F# where the compiler automatically turns a value created using the lambda function syntax fun x -> .. to a value of type Expression<...>. It does not do this for arguments of let-bound functions, but it does do this for the arguments of static methods. This means that you can use:

open System
open System.Linq.Expressions

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

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

Upvotes: 8

Related Questions