我正在为SQL编写F#dsl(http://github.com/kolosy/furious)。
select语句如下所示:
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') @>
显而易见(又愚蠢)的问题是......如何对报价进行参数化?
如果我只是喜欢:
let z = "60614"
let (neighbor: person seq) =
db.Yield <@ Seq.filter (fun p -> p.homeAddress.zip = z) @>
然后z
被解析为静态属性访问器(PropertyGet(None, String z, [])
)。我需要一些东西让我只根据报价检索变量/ let绑定的值。想法?
答案 0 :(得分:6)
报价不是我的强项,但请查看其中的区别:
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
我认为可能'z'在表达式中是局部的意味着捕获了值,而不是属性引用。
答案 1 :(得分:4)
除了Brian写的内容之外 - 我认为访问全局let
绑定值的编码也非常稳定,并且很可能在将来继续编码为PropGet
。 / p>
这意味着您可以在翻译器中明确支持此案例,并添加一个简单的预处理步骤来获取这些属性的值。这可以使用ExprShape
来完成(这允许您仅使用4个案例完全遍历引用)。这将允许您的DSL支持一般情况。
以下函数遍历引号并用其值替换对全局let
的访问权限:
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)
然后,您可以编写以下内容以获取包含值而不是PropGet
的报价:
let z = 5
let eOrig = <@ Seq.filter (fun p -> p = z) [ 1 .. 10 ]@>
let eNice = expand eOrig