4

我正在为 SQL ( http://github.com/kolosy/furious ) 编写一个 F# dsl。

选择语句如下所示:

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 绑定的值的东西。想法?

4

2 回答 2

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'在表达式中是本地的,这意味着值被捕获,而不是属性引用。

于 2010-07-12T22:08:11.393 回答
4

除了 Brian 所写的内容之外 - 我相信访问全局let绑定值的编码也非常稳定,并且它们很可能会像将来一样继续被编码PropGet

这意味着您可以在您的翻译器中明确支持这种情况,并添加一个简单的预处理步骤来获取这些属性的值。这可以使用ExprShape(它允许您仅使用 4 个案例完全遍历引用)来完成。这将允许您的 DSL 也支持一般情况。

以下函数遍历引号并将对全局lets 的访问替换为它们的值:

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)

然后您可以编写以下内容来获取包含 value 而不是 的引用PropGet

let z = 5
let eOrig = <@ Seq.filter (fun p -> p = z) [ 1 .. 10 ]@> 
let eNice = expand eOrig
于 2010-07-12T23:14:27.537 回答