hubFS: THE place for F#

. . . are you on The Hub?
Welcome to hubFS: THE place for F# Sign in | Join | Help
in Search

My F# Notes

F# quotations visualizer - reloaded!

Quotation Visualizer

Some time ago, I wrote an article about useful utility called F# quotations visualizer. This utility can be used to show visual representation of F# quotations, which can represent (subset of) source code written in F#. There are two ways that you can use to get F# quotations - first is using operators <@@ ... @@> (this returns quotation of the code written inside the operator), second method is to get quotation of top level definition from compiled F# assembly (you have to explicitly enable this using command line switch --quotation-data while compiling assembly).

Because I added several new features to the original Quotations visualizer, I decided to publish the latest version - here is the list of main improvements:

  • Application is rewritten using active patterns (new F# language feature)
  • It is possible to extract quotations from compiled F# assembly (if it contains quotation data)
  • Added support for several missing language constructs

Active patterns

Active patterns is a new (experimental) feature in the F# language. You can find some information about this feature in Don Syme's article [1]. In simple words, active patterns allows you to write "switch" consisting of functions (patterns) that return 'a option type. When the code is executed, it finds first pattern whose query returned a value (Some(...)) and the body of selected pattern is executed.

This feature makes it very easy to work with F# quotations because all ef[Something] values that are used for querying quotations have the required signature (the Query function that returns 'a option type), so they can be used with the active patterns feature.

In the quotations visualizer, we need to match the expression (type expr) with all possible expression families (ef[Something]) and choose the first one that matches. To see how active patterns work, you can look at the following part of the function that does this. First, without active patterns:

match efForLoop.Query(expr) with
  | Some(nfrom,nto,body) ->
      // Statement: for i=start to end do body; done;
      // .. create tree node ..
  | _ ->
match efWhileLoop.Query(cond,body) with
      // Statement: while condition do body; done;
      // .. create tree node ..
  | _ ->
match efCond.Query(t,(cond,trbody,flbody)) with
      // if (cond) then trbody; else flbody;
      // .. create tree node ..
  | _ ->
      // unknown expression

And with active patterns the source looks like this:

#light
let EFForLoop = efForLoop
let EFWhileLoop = efWhileLoop
let EFCond = efCond

match expr with
  | EFForLoop(nfrom,nto,body) ->
      // Statement: for i=start to end do body; done;
      // .. create tree node ..
  | EFWhileLoop(cond,body) ->
      // Statement: while condition do body; done;
      // .. create tree node ..
  | EFCond(t,(cond,trbody,flbody)) ->
      // if (cond) then trbody; else flbody;
      // .. create tree node ..
  | _ ->
      // unknown expression

This is very simple example and there are many situations where active patterns are even more helpful. You may have also noticed that the code doesn't contain any semicolons. This is the result of another new feature called lightweight syntax (it is turned on by the #light directive) - if you turn it on the whitespace becomes significant and compiler can understand structure of the code using whitespace instead of semicolons, parentheses and begin/end keywords. This feature is described in the F# manual [2].

Extracting quotations from assembly

When you specify --enable-quotation-data switch to the F# compiler, it stores quotation of every top level definition (functions in modules) in the assembly. This quotation can be later retrieved using resolveTopDef function, however to use the function for loading top-level definitions from another assembly, you first have to load quotation data from the assembly. The following snippet shows how to do this.

let asm = Assembly.LoadFile(name) in
  asm.GetManifestResourceNames() |> Array.to_list 
    |> List.filter (fun rn -> 
        rn.StartsWith(pickledDefinitionsResourceNameBase)) 
    |> List.iter (fun rn -> 
        explicitlyRegisterTopDefs asm rn (readToEnd (asm.GetManifestResourceStream(rn))))

This code first loads the assembly (using LoadFile method which accepts assembly file path). Than it gets all managed resources of the assembly and selects only those resources, whose name starts with pickledDefinitionsResourceNameBase (this is a constant declared in Microsoft.FSharp.Quotations.Raw module). Now we have all resources containing F# quotation data, and we can use explicitlyRegisterTopDefs function to load quoted top level definitions. The explicitlyRegisterTopDefs method takes three parameters - assembly, name of the resource and resource data (byte array). When the top level definitions are registered using this method, it is possible to load quotations of functions declared in loaded assembly - and this is exactly what happens when you click on the "Open F# assembly" link in the application. If you are interested in the complete code, look at the attached source code of quotations visualizer application.

Links

Downloads

Published Sunday, October 01, 2006 10:02 PM by tomasp

Comments

 

dsyme said:

Great article! And it's great to see the experimental active patterns feature being picked up in this way.
October 1, 2006 3:18 PM
 

Jason Haley said:

October 2, 2006 8:50 PM
 

Andreas said:

Great article, but the downloads doesn't work.
October 3, 2006 12:57 AM
 

tomasp said:

Thanks for pointing out, my mistake!
Downloads are fixed.
October 3, 2006 2:53 AM
Anonymous comments are disabled

This Blog

Post Calendar

<October 2006>
SuMoTuWeThFrSa
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

Syndication

Powered by Community Server, by Telligent Systems