F#报价的部分应用

时间:2017-01-26 23:46:54

标签: f# metaprogramming quotations

假设我有Quotations.Expr<(int -> int -> int)>

类型的报价
<@ fun x y -> x + y @>

我想创建一个函数fun reduce x expr,当调用reduce 1 expr时基本上会产生

<@ fun y -> 1 + y @>

即。我想部分应用报价来产生另一个报价。

我确信这是可行的,有没有人有任何想法?以前是否曾尝试过?似乎找不到任何东西。

此外,我对LISP不是很熟悉 - 但这与我用LISP宏实现的基本相似吗?

更新: 在减少报价的同时,我想评估可以在结果表达式树中评估的零件。

例如:reduce true <@ fun b x y -> if b then x + y else x - y@>应该会产生<@ fun x y -> x + y @>

2 个答案:

答案 0 :(得分:4)

如果您知道自己的报价格式为import 'rxjs/add/operator/withLatestFrom'; var campaignSelected$ = this.store.select( store => store.appDb.uiState.campaign.campaignSelected ); var campaignsList$ = this.store.select( store => store.msDatabase.sdk.table_campaigns ); return campaignSelected$.withLatestFrom( campaignsList$, (campaignId, campaigns) => { ... } ); ,那么很简单:

fun x ...

如果您还想简化表达式,那么您需要回答一些棘手的问题:

  • 你关心副作用吗?如果我从let subst (v:'a) (Patterns.Lambda(x,b) : Expr<'a->'b>) = b.Substitute(fun x' -> if x = x' then Some (Expr.Value v) else None) |> Expr.Cast<'b> subst 1 <@ fun x y -> x + y @> 开始,并在<@ fun x y -> printfn "%i" x @>中替换为1,那么x的简化版本是什么?这应该在每次调用时打印出<@ fun y -> printfn "%i" 1 @>,但除非您提前知道哪些表达式可能会导致副作用,否则您几乎不会简化任何事情。如果你忽略这一点(假设没有表达会导致副作用)那么事情变得更加简单,但代价是保真度。
  • 简化到底是什么意思?让我们说我在替换后得到1。然后,将此简化为等同于<@ fun y -> y + 1 @>是好还是坏?这绝对是更简单的&#34;因为它只是一个包含一个值的普通表达式,但该值现在是一个不透明的函数。如果我有let f y = y+1 in <@ f @>怎么办?是否可以将内部函数简化为值,还是不好?

如果我们可以忽略副作用并且我们不想用一个值替换一个函数,那么你可以定义一个这样的简化函数:

<@ fun y -> 1 + (fun z -> z) y @>

请注意,这仍然可能不像您所希望的那样简化;例如let reduce (e:Expr<'a>) : Expr<'a> = let rec helper : Expr -> Expr = function | e when e.GetFreeVars() |> Seq.isEmpty && not (Reflection.FSharpType.IsFunction e.Type) -> // no free variables, and won't produce a function value Expr.Value(Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation e, e.Type) | ExprShape.ShapeLambda(v, e) -> Expr.Lambda(v, helper e) // simplify body | ExprShape.ShapeCombination(o, es) -> // simplify each subexpression ExprShape.RebuildShapeCombination(o, es |> List.map helper) | ExprShape.ShapeVar v -> Expr.Var v helper e |> Expr.Cast 将不会被简化,但<@ (fun x (y:int) -> x) 1 @>将是。

答案 1 :(得分:2)

拼接是在引号中嵌入引号的便捷方式:

let reduce x expr =
    <@ (%expr) x @>

reduce的类型为'a -> Expr<('a -> 'b)> -> Expr<'b>

用法:

let q = <@ fun x y -> x + y @>
let r = reduce 1 q // Expr<int -> int>
let s = reduce 2 <| reduce 3 q // Expr<int>
let t = reduce "world" <@ sprintf "Hello %s" @> // Expr<string>