5

In C# I could create a string representation of an object graph fairly easily with expression trees.

public static string GetGraph<TModel, T>(TModel model, Expression<Func<TModel, T>> action) where TModel : class
{
            var method = action.Body as MethodCallExpression;
            var body = method != null ? method.Object != null ? method.Object as MemberExpression : method.Arguments.Any() ? method.Arguments.First() as MemberExpression : null : action.Body as MemberExpression;
            if (body != null)
            {
                string graph = GetObjectGraph(body, typeof(TModel))
                return graph;
            }
            throw new Exception("Could not create object graph");
}

In F# I've been looking at Quotations to attempt to do the same thing, and can't quite figure it out. I've attempted converting the quotation into an Expression using the PowerPack libraries, but have had no luck so far, and the information on the internet seems fairly sparse on this topic.

If the input is:

let result = getGraph myObject <@ myObject.MyProperty @>

the output should be "myobject.MyProperty"

Stephen Swensen
  • 22,107
  • 9
  • 81
  • 136
justin
  • 453
  • 1
  • 4
  • 13
  • 1
    Dunno the answer (quotations are not my forte), but want to ensure you've read http://blogs.msdn.com/dsyme/archive/2009/10/23/a-quick-refresh-on-query-support-in-the-f-power-pack.aspx which are the best current docs about the Quotation->Expression support in the PowerPack. – Brian Dec 18 '09 at 22:14

2 Answers2

5

You can see what you get from quotation expression in fsi session:

> let v = "abc"
> <@ v.Length @>;;
val it : Expr<int>
= PropGet (Some (PropGet (None, System.String v, [])), Int32 Length, [])

> <@ "abc".Length @>;;
val it : Expr<int>
= PropGet (Some (Value ("abc")), Int32 Length, [])

You can find description of all active patterns available to parse qoutations into

manual\FSharp.Core\Microsoft.FSharp.Quotations.Patterns.html

under your F# installation directory or at msdn site

There is nice Chris Smith's book "Programming F#" with chapter named "Quotations" :)

So, after all, just try to write simple parser:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns

let rec getGraph (expr: Expr) =
  let parse args =
    List.fold_left (fun acc v -> acc ^ (if acc.Length > 0 then "," else "") ^ getGraph v) "" args
  let descr s = function
    | Some v -> "(* instance " ^ s ^ "*) " ^ getGraph v
    | _ -> "(* static " ^ s ^ "*)"
  match expr with
  | Int32 i -> string i
  | String s -> sprintf "\"%s\"" s
  | Value (o,t) -> sprintf "%A" o
  | Call (e, methodInfo, av) ->
    sprintf "%s.%s(%s)" (descr "method" e) methodInfo.Name (parse av)
  | PropGet(e, methodInfo, av) ->
    sprintf "%s.%s(%s)" (descr "property" e) methodInfo.Name (parse av)
  | _ -> failwithf "I'm don't understand such expression's form yet: %A" expr

P.S. And of course you will need some code to translate AST to human readable format.

ssp
  • 1,702
  • 9
  • 11
  • fyi - these code examples are out of date will not compile against F# 2.0+ (List.fold_left is now List.fold, PropGet is now PropertyGet, and ocaml-style string concat (^) produces warnings. – Stephen Swensen Feb 27 '11 at 23:18
4

I'm unsure what the state of things was back when you asked this question, but today you can convert an F# Quotation to an Expression using the PowerPack like so:

<@ "asdf".Length @>.ToLinqExpression()

Also, I've been developing a library Unquote which is able to decompile many F# Quotations into F# single-line non-light syntax code. It can easily handle simple instance PropertyGet expressions like your required input / output:

> decompile <@ "asdf".Length @>;;
val it : string = ""asdf".Length"

See my answer to a similar question for more information or just visit Unquote's home page.

Community
  • 1
  • 1
Stephen Swensen
  • 22,107
  • 9
  • 81
  • 136