开发者

F# Quotations - traversing into function calls represented by a Value

I've spent a few hours trying to get to grips with F# Quotations, but I've come across a bit of a road block. My requirement is to take simple functions (just integers,+,-,/,*) out of a discriminated union type and generate an expression tree that will eventually be used to generate C code. I know this is possible using Quotations with 'direct' functions.

My problem is that the expression tree seems to terminate with a "Value", and I can't figure out how to traverse into that value.

My questions is whether this is actually possible in this situation? or are there any other approaches that are worth considering.

type FuncType = 
| A of (int -> int -> int)
| B
| C

[<ReflectedDefinition>]
let add x y = x + y


let myFunc1 = A (fun x y -> x + y )
let myFunc2 = A add 

let thefunc expr = 
    match expr with
    | A(x) ->
        <@ x @>
    | _ ->
        failwith "fail"

printfn "%A" (thefunc myFunc1) // prints "Value (<fun:myFunc1@14>)"
printfn "%A" (thefunc myFunc2) // prints "Value (<fun:myFunc2@15>)"
printfn "%A" <@ fun x y -> x + y @> // generates usable expressi开发者_开发问答on tree


Quotations represent the F# code that was quoted syntactically. This means that if you write something like <@ x @>, the quotation will contain just Value case specifying that you quoted something which has the specified value. (Variables are automatically replaced with values if the variable is defined outside of the quotation).

You can only get quotation of code that was explicitly quoted using <@ .. @> or of a function that was marked as ReflectedDefinition and is referred to by name in a quotation (e.g. <@ add @> but not for example let f = add in <@ f @>).

To be able to do what your snippet suggests, you'll need to store quotations in your FuncType too (so that the lambda function that you write is also quoted and you can get its body). Something like:

type FuncType = 
  | A of Expr<int -> int -> int>
  | B | C

[<ReflectedDefinition>]
let add x y = x + y

let myFunc1 = A <@ fun x y -> x + y @>
let myFunc2 = A <@ add @>

let thefunc expr = 
    match expr with
    | A(x) -> x
    | _ -> failwith "fail"

This should work for functions marked as ReflectedDefinition too. To extract the body of the function you need to add something like (you'll need to substitute arguments of the function for parameters, but this should give you some idea):

match expr with
| Lambdas(_, body) -> 
    match body with 
    | Call(_, mi, _) when Expr.TryGetReflectedDefinition(mi) <> None ->
      let func = Expr.TryGetReflectedDefinition(mi)
      match func with 
      | Some(Lambdas(_, body)) ->
          // 'body' is the quotation of the body
      | _ -> failwith "Not supported function"
    | _ -> failwith "Not supported function"
| _ -> failwith "Not supported expression"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜