If you know that your quote is in the form of fun x ...
then this is easy:
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 xy -> x + y @>
If you also want to simplify the expressions, then there are a few few difficult questions that you need to answer:
- Do you care about side effects? If I start with
<@ fun xy -> printfn "%i" x @>
and substitute 1
for x
, then what is the simplified version of <@ fun y -> printfn "%i" 1 @>
? This should print 1
every time it is called, but if you don't know in advance which expressions can cause side effects, you can almost never simplify anything. If you ignore this (if no expression causes side effects), then things become much easier due to fidelity. - What does simplification mean? Say I get
<@ fun y -> y + 1 @>
after wildcard. Then is it good or bad to simplify this to the equivalent of let fy = y+1 in <@ f @>
? This is definitely βsimplerβ because it is a trivial expression containing only a value, but the value is now an opaque function. What if I have <@ fun y -> 1 + (fun z -> z) y @>
? Is it possible to simplify an internal function to a value or bad?
If we can ignore the side effects, and we never want to replace the function with a value, then you can define a simplifying function like this:
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
Please note that this all the same may not simplify the work as much as you would like; for example, <@ (fun x (y:int) -> x) 1 @>
will not be simplified, although <@ (fun x -> x) 1 @>
will.
source share