Partial use of F # quotes

Let's say I have this sentence like Quotations.Expr<(int -> int -> int)>

 <@ fun xy -> x + y @> 

I want to create a fun reduce x expr function that, when you call reduce 1 expr , will essentially give

 <@ fun y -> 1 + y @> 

i.e. I want to partially apply a quote to create another quote.

I'm sure this is doable, does anyone have any thoughts? Has this been done before? Seems nothing can find.

I am also not very familiar with LISP - but does it essentially resemble what I can achieve with LISP macros?

UPDATE: When shortening the quote, I would like to evaluate the parts that can be evaluated in the resulting expression tree.

For example: reduce true <@ fun bxy -> if b then x + y else x - y@ > should result in <@ fun xy -> x + y @> .

+6
source share
2 answers

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.

+4
source

Bonding is a convenient way to embed citations into quotes:

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

reduce is of type 'a -> Expr<('a -> 'b)> -> Expr<'b>

Using:

 let q = <@ fun xy -> 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> 
+2
source

Source: https://habr.com/ru/post/1014478/


All Articles