给出以下类型
type Foo = { foo: string; bar: int };;
和以下代码引用
<@fun v x -> { x with foo = v; bar = 99 } @>;;
这将导致
val it : Quotations.Expr<(string -> Foo -> Foo)> =
Lambda (v, Lambda (x, NewRecord (Foo, v, Value (99))))
预期。还有以下代码引用
<@fun v x -> { x with bar = v;foo = "foo" } @>;;
产生预期结果。
val it : Quotations.Expr<(int -> Foo -> Foo)> =
Lambda (v, Lambda (x, NewRecord (Foo, Value ("foo"), v)))
然而这(改变顺序和将值分配给第二个字段)
<@fun v x -> { x with bar = 66;foo = v } @>;;
产量
val it : Quotations.Expr<(string -> Foo -> Foo)> =
Lambda (v, Lambda (x, Let (bar, Value (66), NewRecord (Foo, v, bar))))
a let
。但代码中没有let
。这是为什么?
答案 0 :(得分:4)
报价仅保证他们能够生成具有正确行为的表达式,而不是任何特定形状。
例如引号<@@ 1 = 2 || 2 = 3 @@>
将生成一个包含if
语句的表达式(即if 1 = 2 then true else 2 = 3
)。
将结果表达式标准化是一个非常深的兔子洞,但你可以在这里看到一些基本的标准化器:https://github.com/mavnn/Algebra.Boolean/blob/master/Algebra.Boolean/Transforms.fs
具体来说,请检查文件末尾的unbind
。
let unbind quote =
let rec findLet q =
match q with
| Let (var, value, body) ->
findLet (replaceVar var.Name value body)
| ShapeLambda (v, e) ->
Expr.Lambda(v, findLet e)
| ShapeVar v ->
Expr.Var v
| ShapeCombination (o, es) ->
RebuildShapeCombination(o, es |> List.map findLet)
and replaceVar name value q =
match q with
| Let (v, e, e') ->
if v.Name = name then
findLet (Expr.Let(v, e, e'))
else
Expr.Let(v, replaceVar name value e, replaceVar name value e')
| ShapeLambda (v, e) ->
Expr.Lambda(v, replaceVar name value e)
| ShapeVar v ->
if v.Name = name then
value
else
Expr.Var v
| ShapeCombination (o, es) ->
RebuildShapeCombination(o, es |> List.map (replaceVar name value))
findLet quote
至于为什么这些具体表达方式有所不同?不知道,我害怕!
答案 1 :(得分:4)
我相信你在这里看到的是记录中with
语法的去糖化的特殊情况。我认为这里发生的事情是使用v捕获值以确保以正确的字段顺序计算表达式。因此在这种情况下引入let绑定,因为传入的参数是正在使用的第二个值。
这来自F# language spec。
原始记录结构是一种详细的形式,其中 字段的显示顺序与记录类型定义中的顺序相同。 记录表达式本身详细说明了可能引入的表单 本地值定义,以确保表达式的计算 字段定义在原始中出现的顺序 表达