Embed a variable inside an F# quotation
I'm writing an F# dsl for SQL (http://github.com/kolosy/fu开发者_开发问答rious).
A select statement would look like this:
type person = {
personId: string
firstname: string
lastname: string
homeAddress: address
workAddress: address
altAddresses: address seq
}
and address = {
addressId: string
street1: string
zip: string
}
let (neighbor: person seq) =
db.Yield <@ Seq.filter (fun p -> p.homeAddress.zip = '60614') @>
The obvious (and silly) question is... How do I parametrize the quotation?
If I just somehting like:
let z = "60614"
let (neighbor: person seq) =
db.Yield <@ Seq.filter (fun p -> p.homeAddress.zip = z) @>
then z
gets resolved into a static property accessor (PropertyGet(None, String z, [])
). I need something that will let me retrieve the value of the variable/let binding based solely on the quotation. Ideas?
Quotations are not my forte, but check out the difference here:
let z = "60614"
let foo = <@ List.filter (fun s -> s = z) @>
printfn "%A" foo
let foo2 =
let z = z
<@ List.filter (fun s -> s = z) @>
printfn "%A" foo2
I think maybe having 'z' be local to the expression means the value is captured, rather than a property reference.
In addition to what Brian wrote - I believe that the encoding of access to global let
bound values is also pretty stable and they will quite likely continue to be encoded as PropGet
in the future.
This means that you could support this case explicitly in your translator and add a simple pre-processing step to get values of these properties. This can be done using ExprShape
(which allows you to fully traverse quotation just using 4 cases). This would allow your DSL to support the general case as well.
The following function traverses quotation and replaces access to global let
s with their value:
open Microsoft.FSharp.Quotations
let rec expand e =
match e with
// Extract value of global 'let' bound symbols
| Patterns.PropertyGet(None, pi, []) ->
Expr.Value(pi.GetValue(null, [| |]), e.Type)
// standard recursive processing of quotations
| ExprShape.ShapeCombination(a, b) ->
ExprShape.RebuildShapeCombination(a, b |> List.map expand)
| ExprShape.ShapeLambda(v, b) -> Expr.Lambda(v, expand b)
| ExprShape.ShapeVar(v) -> Expr.Var(v)
Then you can write the following to get a quotation that contains value instead of PropGet
:
let z = 5
let eOrig = <@ Seq.filter (fun p -> p = z) [ 1 .. 10 ]@>
let eNice = expand eOrig
精彩评论