Incorpora una variabile all'interno di una citazione F #
-
14-09-2020 - |
Domanda
Sto scrivendo un f # dsl per sql ( http://github.com/kolosy/furious).
A Seleziona affermazione sarà simile a questo:
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') @>
.
La domanda ovvia (e sciocco) è ... come faccio parametrizzare la citazione?
Se ho solo un po 'come:
let z = "60614"
let (neighbor: person seq) =
db.Yield <@ Seq.filter (fun p -> p.homeAddress.zip = z) @>
.
Allora z
viene risolto in un accessorio di proprietà statico (PropertyGet(None, String z, [])
).Ho bisogno di qualcosa che mi permetterà di recuperare il valore della variabile / lasciare il legame basato esclusivamente sulla citazione.Idee?
Soluzione
Citazioni non sono il mio forte, ma controlla la differenza qui:
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
.
Penso che forse avere "z" essere locale all'espressione significa che il valore viene catturato, piuttosto che un riferimento di proprietà.
Altri suggerimenti
Oltre a ciò che Brian ha scritto - credo che la codifica dell'accesso ai valori globali generacoli di let
sia anche piuttosto stabile e probabilmente continueranno a essere codificata come PropGet
in futuro.
Ciò significa che è possibile supportare questo caso esplicitamente nel traduttore e aggiungere un semplice passo di pre-elaborazione per ottenere valori di queste proprietà.Questo può essere fatto usando ExprShape
(che consente di trasformare completamente la quotazione utilizzando 4 casi).Ciò consentirebbe al tuo DSL di supportare anche il caso generale.
La seguente funzione attraversa la quotazione e sostituisce l'accesso a generatori globali con il loro valore:
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)
.
Quindi è possibile scrivere quanto segue per ottenere un preventivo che contiene valore anziché let
:
let z = 5
let eOrig = <@ Seq.filter (fun p -> p = z) [ 1 .. 10 ]@>
let eNice = expand eOrig
.