Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the AST of functions in a [<ReflectedDefinition>] tagged module?

Tags:

f#

[<ReflectedDefinition>]
module Foo = 
    let x = 5
    let y () = 6
    let z a = a

I tried to find out how to get the AST in this situation a couple of times now and keep failing. Time to ask the question here.

So far, I thought that a module would be mappped to a class with static members internally and as such, it should be the equivalent of:

[<ReflectedDefinition>] 
type Foo =
    static member x = 5
    static member y () = 6
    static member z a = a

let bar_members = 
    typeof<Bar>.GetMethods()
    |> Array.filter (fun mi -> match mi with | MethodWithReflectedDefinition x -> true | _ -> false)
    |> Array.map (fun m -> sprintf "%s: %A" (m.Name) (Expr.TryGetReflectedDefinition(m :> MethodBase) ) )

In the latter case, I could use typeof<Foo>.GetMembers() (or GetMethods()?!), cast it to Reflection.MethodBase and use this as an argument for Expr.TryGetReflectedDefinition().

But unfortunately, this is not working with the module version.

So, how to do it?

If you want to play with the code, you might want to open some namespaces:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Reflection
open System.Reflection
like image 321
BitTickler Avatar asked Feb 23 '16 11:02

BitTickler


1 Answers

The problem comes go down to actually getting the type of the Module. In order to do that, there's a great answer here by Phillip Trelford: https://stackoverflow.com/a/14706890/5438433

Basically, you add a helper value to your module which returns the type of that module:

[<ReflectedDefinition>]
module Foo = 
    type internal IMarker = interface end
    let fooType = typeof<IMarker>.DeclaringType

    let x = 5
    let y () = 6
    let z a = a

You can then use fooType to retrieve the reflected definitions.

 let foo_members = 
     Foo.fooType.GetMethods()
     |> Array.filter (fun mi -> match mi with | MethodWithReflectedDefinition x -> true | _ -> false)
     |> Array.map (fun m -> sprintf "%s: %A" (m.Name) (Expr.TryGetReflectedDefinition(m :> MethodBase) ) )

I can then, e.g. print the results:

[|"get_fooType: Some PropertyGet (Some (Call (None, TypeOf, [])), DeclaringType, [])"; "get_x: Some Value (5)"; "y: Some Lambda (unitVar0, Value (6))"; "z: Some Lambda (a, a)"|]

like image 152
TheInnerLight Avatar answered Oct 19 '22 23:10

TheInnerLight