tej*_*jas 6 f# metaprogramming quotations
假设我有这种类型的报价 Quotations.Expr<(int -> int -> int)>
<@ fun x y -> x + y @>
Run Code Online (Sandbox Code Playgroud)
我想创建一个函数fun reduce x expr,当被调用时,reduce 1 expr本质上会产生
<@ fun y -> 1 + y @>
Run Code Online (Sandbox Code Playgroud)
即我想部分应用报价来产生另一个报价.
我确信这是可行的,有没有人有任何想法?以前是否曾尝试过?似乎找不到任何东西.
另外我对LISP不是很熟悉 - 但这与我用LISP宏实现的基本相似吗?
更新:在减少报价时,我想评估可以在结果表达式树中评估的零件.
例如:reduce true <@ fun b x y -> if b then x + y else x - y@>应该导致<@ fun x y -> x + y @>.
如果您知道您的报价采用以下形式,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 @>
Run Code Online (Sandbox Code Playgroud)
如果您还想简化表达式,那么您需要回答一些稍微棘手的问题:
<@ fun x y -> printfn "%i" x @>并替换1为x,那么 的简化版本是什么<@ fun y -> printfn "%i" 1 @>?每次调用它时都应该打印出来1,但是除非您提前知道哪些表达式可能会导致副作用,否则您几乎永远无法简化任何事情。如果您忽略这一点(假设没有表达式会产生副作用),那么事情会变得更加简单,但会牺牲保真度。<@ fun y -> y + 1 @>在替换后得到了。那么,将其简化为等价物是好是坏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
Run Code Online (Sandbox Code Playgroud)
请注意,这仍然可能不会像您希望的那样简化事情;例如,<@ (fun x (y:int) -> x) 1 @>虽然会被简化,但<@ (fun x -> x) 1 @>不会被简化。