I'm using F# 3.0 with .NET 4.5 beta, and I'm trying to convert an F# quotation of type Expr<'a -> 'b> to a LINQ Expression<Func<'a, 'b>>.
I've found several questions that have solutions to this problem, but those techniques don't seem to work any longer, presumably due to changes in either F# 3.0 or .NET 4.5.
Converting F# Quotations into LINQ Expressions
Expression<Func<T, bool>> from a F# func
In both cases, when I run the code from the solutions of either question, the following action throws an exception:
mc.Arguments.[0] :?> LambdaExpression
...where mc is a MethodCallExpression. The exception is:
System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'.
No, the extra "N" at the end of MethodCallExpressionN is not a typo. Does anyone have a suggestion? Thanks.
UPDATE
Here's a complete reproduction. It turns out this code works fine on an expression like <# fun x -> x + 1 #>. My problem is that in my case I need to convert an Expr<'a -> 'b> into Expr<'a -> obj> so that I don't have to litter all my lambda expressions with box. I did so by splicing the original expression into this one: <# %exp >> box #>. This produces an object with the correct type, but the code to convert to Expression<Func<'a, obj>> no longer works.
module Expr =
open System
open System.Linq.Expressions
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.QuotationEvaluation
let rec private translateExpr (linq:Expression) =
match linq with
| :? MethodCallExpression as mc ->
let le = mc.Arguments.[0] :?> LambdaExpression
let args, body = translateExpr le.Body
le.Parameters.[0] :: args, body
| _ -> [], linq
let ToFuncExpression (expr:Expr<'a -> 'b>) =
let args, body = expr.ToLinqExpression() |> translateExpr
Expression.Lambda<Func<'a, 'b>>(body, Array.ofList args)
let exp = <# fun x -> x + 1 #>
let r = Expr.ToFuncExpression <# %exp >> box #>
printfn "%A" r
Can you post a more complete sample and also include the F# expression that you're trying to convert?
I tried to test the behaviour on .NET 4.5 using a minimal sample and it worked for me. Here is what I did:
I created new F# 3.0 project and copied Linq.fs and Linq.fsi from the 2.0 version of F# PowerPack. (Or is there a 3.0 version of the ToLinqExpression method available somewhere in F# 3.0?)
I used the code from Daniel's earlier answer and called the function as follows:
let r = toLinq <# fun x -> x + 1 #>
printfn "%A" r
This did not throw any exception and it printed x => (x + 1), which looks correct to me.
EDIT: To answer the updated question - both of the code samples that you referred to (mine and Daniel's) assume that the body of the quotation is an explicitly constructed function, so they only work on quotations of a specific structure: <# fun x -> ... #>.
You can fix the problem by using splicing in an explicitly constructed function. The following works for me:
let exp = <# fun x -> x + 1 #>
let r = toLinq <# fun a -> box ((%exp) a) #>
printfn "%A" r
This contains application of an F# function, so the generated Expression contains a call to ToFSharpFunc (which converts a delegate to an F# function) and then invocation of this. This may be an issue if you want Expression that standard .NET tools can understand (in which case, you'd have to post-process the C# expression tree and remove these constructs).
Related
The following code is extracted from an application and adapted to highlight the issue as easy as possible
module Mo
open System
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
open System.Linq.Expressions
type Type() =
member _.Prop1 with get() = 1
member _.Prop2 with get() = 2
let toFunc<'t when 't :> Type>(filter: 't -> Expr<bool>) =
let xp = <# Func<'t, bool>(fun (t: 't) -> %(filter t) && t.Prop2 = 2) #>
LeafExpressionConverter.QuotationToExpression xp |> unbox<Expression<Func<'t, bool>>>
let getFunc (i: int) =
let filter (t: Type) = <# t.Prop1 = i #>
toFunc filter
the problem is in the line
let xp = <# Func< 't, bool>(fun (t: 't) -> %(filter t) && t.Prop2 = 2) #>
The compiler complains in fun (t: 't) as follows:
Error FS0446
The variable 't' is bound in a quotation but is used as part of a spliced expression.
This is not permitted since it may escape its scope.
The intent is to compose quotations into a filter Linq expression.
Is there a (alternative) way to do this?
EDIT:
Some more context looks necessary:
The returned Func expression is later passed to Azure.Data.Tables.TableClient.Query(). Unfortunately this method doesn't support expressions containing function calls.
Converting filter to a quoted function (as suggested by Fyodor) was my first version, but I had to abandon it because of this requirement of the Azure Tables SDK.
So the question becomes :
Is it possible to achieve the result of an expression that doesn't contain calls to external method/function?
You're mixing up your variables between quotation realm and "regular" realm.
filter is not a quoted function. It's a regular function that returns a quotation. Regular functions get regular parameters, quoted functions get quoted parameters.
The t parameter here:
let xp = <# Func<'t, bool>(fun (t: 't) -> %(filter t) && t.Prop2 = 2) #>
^
|
This one
That's a quoted parameter. It's defined inside a quotation.
And yet, you're trying to pass its value to the filter function. But at the moment of constructing the quotation, the parameter t doesn't have a value yet! Only when you're done constructing the quotation, then compile it to IL, and then call it, - only then will the parameter t have a value, allowing you to call the filter function. But you need the result of filter function to finish constructing the quotation in the first place!
The most straightforward fix is to make filter a quoted function. Then you can splice it into the quotation, and then pass the parameter t to the result of splicing:
let toFunc<'t when 't :> Type>(filter: Expr<'t -> bool>) =
let xp = <# Func<'t, bool>(fun (t: 't) -> (%filter) t && t.Prop2 = 2) #>
LeafExpressionConverter.QuotationToExpression xp |> unbox<Expression<Func<'t, bool>>>
let getFunc (i: int) =
let filter = <# fun (t: Type) -> t.Prop1 = i #>
toFunc filter
Currently, I'm trying to teach myself some F# by making an application that consists of a C# GUI layer and an F# business layer. In the GUI layer, the user will at some point have to make a choice by selecting a value that is part of a simple enum, e.g. selecting either of the following:
enum {One, Two, Three}
I have written a function to translate the enum value to an F# discriminated union
type MyValues =
| One
| Two
| Three
Now I have to translate back, and am already tired of the boilerplate code. Is there a generic way to translate my discriminated union to the corresponding enum, and vice versa?
Cheers,
You can also define the enum in F# and avoid doing conversions altogether:
type MyValues =
| One = 0
| Two = 1
| Three = 2
The = <num> bit tells the F# compiler that it should compile the type as a union. When using the type from C#, this will appear as a completely normal enum. The only danger is that someone from C# can call your code with (MyValues)4, which will compile, but it will cause incomplete pattern match exception if you are using match in F#.
Here are generic DU/enum converters.
open Microsoft.FSharp.Reflection
type Union<'U>() =
static member val Cases =
FSharpType.GetUnionCases(typeof<'U>)
|> Array.sortBy (fun case -> case.Tag)
|> Array.map (fun case -> FSharpValue.MakeUnion(case, [||]) :?> 'U)
let ofEnum e =
let i = LanguagePrimitives.EnumToValue e
Union.Cases.[i - 1]
let toEnum u =
let i = Union.Cases |> Array.findIndex ((=) u)
LanguagePrimitives.EnumOfValue (i + 1)
let du : MyValues = ofEnum ConsoleColor.DarkGreen
let enum : ConsoleColor = toEnum Three
It maps the DU tag to the enum underlying value.
Why does the F# compiler complain "RequireQualifiedAccess ..." for the Open statement and give an error for the use of List.map in:
open Microsoft.FSharp.Collections.Map
type Gen =
static member Calc (data : int[]) = data.List.map (fun x -> x + 1)
First of all, your open statement has nothing to to with List.map, it would open the Map module, which you cannot open but have to access explicitly with Map., hence the error. The Map module contains functions similar to the ones in the List module, but works with maps (similar to dictionaries in C#).
The function List.map ist just called that: List.map. It is standalone and not a part of your data object, which, by the way, you have defined to be an array with (data : int[]).
So I think the code you meant to write is:
type Gen =
static member Calc (data : List<int>) = data |> List.map (fun x -> x + 1)
And also note that the compiler is smart enough to deduce that data is a list of ints, so you can remove the type annotation if you like.
I'm building a symbolic derivative engine. For example
let f = <# fun x:double -> x * x #>
let df = der f
and the resulting expression will be
<# 2 * x #>
The actual equations could be arbitrarily complex.
The generation of the derivatives are not too hard using recursive
pattern matching and transformations but in the end I want to use
the generated equations in tight numerical loops as if I had hand
written them. This is numerical computing code so faster is always
better ( if possible )
I've looked at the FSharpX quotation compiler but it looks like an interpreter rather than a compiler.
I have not tested this, but the code that translates F# quotations to LINQ expressions (and compiles them) has now moved from F# PowerPack into the F# Core library, so I think that is the most up-to-date version:
open Microsoft.FSharp.Linq.RuntimeHelpers
LeafExpressionConverter.EvaluateQuotation <# 1 + 2 #>
and to use it for lambdas
let d=LeafExpressionConverter.EvaluateQuotation <# fun y -> y+1.0 #>
:?> ( double -> double )
Console.WriteLine(d 10)
outputs
11
Note the cast at the end to convert the ''obj'' to a lambda of the correct type
I'm working on a project in which I'm trying to use F# and Linq for UDF's and stored procs in an SQL server.
Part of that has been to statically define all the valid queries, the sorting criteria, and a means of scoring the results of the queries.
I've so far been fairly successful, but I'm running into serious difficulty composing sortBy expressions.
Here's the basic concept
let sorter =
let exprMap:Map<string,Quotations.Expr<seq<Product> -> seq<Product>>> =
Map.ofList
["ProductName",<# Seq.sortBy (fun prod -> prod.Name) #> ]
// .. more entries ..
let sortBuilder sortkeys =
Array.foldBack
(fun criteria acc -> <# %(exprMap.[criteria]) >> (%acc) #>)
sortkeys
<# Seq.map id #>
This ends up being used later in the query executor like so
let execQuery = fun (predicates,sorts,scorer) ->
<# seq { for prod in (%dc).Products do
if (%predicates) prod then yield prod }
|> (%sorts)
|> (%scorer) #>
Using these basic outlines, everything works as long as I don't use (%sorts). Each time I pass that in, I get not recognized in F# to Linq translator. I've tried a number of different attempts at using combinators, but I have the sense I'm missing something. If I stub out the sorter function with the following
<# Seq.sortBy (fun prod -> prod.Name) |> Seq.sortBy (fun prod -> prod.Style) #>
It works as expected. However using a combinator like this:
let (|>*) = fun f g -> <# fun c -> ((%f) c) |> (%g) #>
does not..
Any ideas?
Unfortunately, I don't have any good answer to this question.
I'm afraid that the F# LINQ translator is currently very sensitive to the structure of the query. Using composition, you should be able to get the same quotation you get if you write it by hand, so you may need to generate exactly the same thing that worked if written by hand.
For example with your sorter, you may need something like (I didn't try it, but I think this should produce exactly the same quotation as the usual code that works):
let (|>*) f g = fun c -> <# (%c) |> (%f) |> (%g) #>
<# seq { for prod in (%dc).Products do
if (%predicates) prod then yield prod } #> |>
( <# Seq.sortBy (fun prod -> prod.Name) #> |>*
<# Seq.sortBy (fun prod -> prod.Style) #> )
The problem is that if you include lambda functions in the quotation, the F# translator needs to deal with them - probably by partially evaluating them (because otherwise the LINQ to SQL translator would fail). There are quite a few tricky cases in this...
However, the F# team has been doing some improvements in this area recently. I think the best thing to do would be to find a simple repro case and send it to fsbugs at microsoft dot com. PowerPack releases are not that "sensitive" so you may be able to get the source code with the recent changes if you ask and offer help with testing (but no promises).