Reputation: 2928
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?
Upvotes: 1
Views: 331
Reputation: 80915
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
Upvotes: 2