在F#语录中嵌入变量

时间:2010-07-12 21:55:29

标签: reflection f# quotations

我正在为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绑定的值。想法?

2 个答案:

答案 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