Expression <Func <T, bool >> from function F #

in linq, .Where takes the Expression> predicate, which I can write in F # as

<@ fun item:'a -> condition @> // Expr<'a -> bool> 

I use FSharp.Powerpack to create an expression from a quote, but what it gives me is a MethodCallExpression expression. Deep looking, powerpack code builds lambda correctly, but wraps it in a Convert call (why?). I am wondering if casting an argument to a method call (lambda) can give me an expression> I need.

So the question is why the Convert call and how to actually get the lambda with the Func signature.

+8
linq f #
source share
2 answers

I can’t remember from my head where I found this bit of code, but this is what I use to convert Expr<'a -> 'b> to Expression<Func<'a, 'b>> . Hope this solves your problem.

 open System open System.Linq.Expressions open Microsoft.FSharp.Quotations open Microsoft.FSharp.Linq.QuotationEvaluation let toLinq (expr : Expr<'a -> 'b>) = let linq = expr.ToLinqExpression() let call = linq :?> MethodCallExpression let lambda = call.Arguments.[0] :?> LambdaExpression Expression.Lambda<Func<'a, 'b>>(lambda.Body, lambda.Parameters) 
+12
source share

One way to do this is to take advantage of the fact that F # will automatically perform this conversion when invoking methods on .NET types that expect Expression<Func<...>> .

I'm not quite sure when this added to the language, but, of course, with F # 4, you do not need to explicitly convert F # expressions to LINQ. If the reason you wanted to do this, first of all, was to be able to use the IQueryable LINQ API (or other expression-based APIs), now it just works effortlessly, for example:

 someEfDataContext.MyEntities.Single(fun e -> e.Id = 42) 

just works. Although it looks like a regular lambda (we did not use the syntax of the F # expression), it compiles into code that creates the F # expression object and then passes it to LeafExpressionConverter‌​.QuotationToExpressi‌on to turn it into a LINQ expression object.

But sometimes you need to get a LINQ style expression object directly in F #. (For example, it is sometimes useful to write an F # function that creates an expression that you will use in several queries.) In this case, you can write an assistant, for example:

 type FunAs() = static member LinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e 

He seems to be doing nothing - he is simply returning his argument. However, since FunAs is a .NET type, F # will automatically compile any call site that calls this with a fun expression into code that generates a suitable LINQ query expression. For example:.

 let linqExpr = FunAs.LinqExpression(fun (e:MyEntity) -> e.Id = 42) 

Here linqExpr will be of type Expression<Func<MyEntity, bool>> .

The key to this is that this method is a member of the .NET Type. If you try the same with the regular F # function:

 let funAsLinqExpression<'T, 'TResult>(e: Expression<Func<'T, 'TResult>>) = e 

which seems to mean the same thing as FunAs.LinqExpression , you will find that you cannot call it the same way. For example, if you try this:

 let linqExpr = funAsLinqExpression(fun (e:MyEntity) -> e.Id = 42) 

You will get a (slightly useless) error: "This function takes too many arguments or is used in a context where the function is not expected."

By providing this function to a member of type .NET, we can take advantage of F # "You seem to be calling the .NET API that expects LINQ type expressions, let me take care of this."

(Perhaps there is an even more explicit way of asking the LINQ compiler to do the same trick for you without introducing the .NET type into it, but I did not find it.)

+4
source share

All Articles