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# - Simple quotations transformation

This article describes very simple code that I wrote while learning how to work with the F# quotations library. Using the F# quotations you can get tree representation of the quoted expression. This allows you to write code that takes code written in F# as data and performs some code analysis or compiles/translates that code to different language. This is very similar to the new C# 3.0 expression trees where you can get expression tree from lambda expression and translate this tree for example to SQL (using DLINQ). However expression trees in C# 3.0 are very limited when compared with F# quotations, so that's one of the many reasons why F# is interesting language.

Introduction to F# quotations

If you want to see how F# represents some expressions you can try it in the FSI (F# interactive). First you need to open three modules that contain functions for working with quotations:

open Microsoft.FSharp.Quotations;;
open Microsoft.FSharp.Quotations.Raw;;
open Microsoft.FSharp.Quotations.Typed;;

Now you can see the internal representation of quotations by writing quoted expression (In VS.Net you can select code and hit Alt+Enter to sent selected code to F# interactive).

<@ 1 + 2 @>

val it : int expr
  = <@ 
      Microsoft.FSharp.MLLib.Pervasives.op_Addition (Int32 1) (Int32 2)
     @>  

One more example with two operators:

<@ 1 + 10/5 @>

val it : int expr
  = <@ 
      Microsoft.FSharp.MLLib.Pervasives.op_Addition (Int32 1)
        (Microsoft.FSharp.MLLib.Pervasives.op_Division (Int32 10) (Int32 5))
     @>

It is not difficult to understand that addition in the first example is represented as function call (function application in the terminology of functional languages) where the function is op_Addition operator and parameters are two integers. This is actually little simplification, because in functional programming you should look at this code as two function applications. The first application binds first parameter of op_Addition to 1 and the result is again function. The second application uses the function returned by the first application and passes 2 as parameter. You can look at F# quotations using both approaches and in the rest of the article I will use the first one.

To demonstrate how to work with F# quotations I decided to write simple transformation of limited mathematical expressions from F# quotations to my data type. The discriminated union that I'm using as target of transformation is similar to the union in F# source file VS.Net template (as you can see it supports only four most basic mathematical operations and all values are stored as integers):

type simple_expr =
  | Int of int
  | Add of simple_expr * simple_expr
  | Sub of simple_expr * simple_expr
  | Mul of simple_expr * simple_expr
  | Div of simple_expr * simple_expr;;

Printing and evaluation of this structure is simple, so I won't describe these here. If you want to look at these functions see attached source code. (function for evaluation is part of VS.Net template too).

Working with quotations

Finally, let's do the interesting part :-). As I mentioned you can get quotations using <@ ... @>. When you want to perform analysis of quotations you need to get the underlying raw representation using the <@@ ... @@> operators. For decomposing the quoted expression we can use Query functions on the expression families defined in the Raw module. This may sound a bit confusing, but in the simple words - the Raw module contains several values that define language constructs (like constants, variables, function application and others) and you can use these values for matching and decomposing quoted expression. Following code should clear this a bit:

let what_is x =
  match Raw.efInt32.Query x with  
    | Some (_) -> "number";
    | _ -> 
  match Raw.efApps.Query x with  
    | Some (_) -> "application";
    | _ -> 
      "something else...";
	    
// Prints "number"	    
print_string (what_is <@@ 1 @@>);

// Prints "application"	    
print_string (what_is <@@ 1 + 2 @@>);

Now we can start writing function that converts mathematical expression quotation to simple_expr structure. It will be recursive function with similar structure as previous. The first match for conversion of numbers will be simple. In the second match we'll have to do two things. First we'll need to determine what function is applied (whether it is addition, subtraction, etc..) and than we'll have to loop through parameters passed to this function and call the function recursively each parameter.

let rec parse x =
  match Raw.efInt32.Query x with  
    // x contains the number so we can simply return Int(x)
    | Some (x) -> Int(x); 
    | _ -> 
  match Raw.efApps.Query x with  
    | Some (op,args) -> 
    
      // op contains quoted reference to function (operator)
      // so we need to extract name of applied function from it
      let opname = (
        match Raw.efAnyTopDefn.Query op with  
          // operators are top-level definitions so we extract name
          // from 'a' that contains info about the definition
          | Some (a,_) -> let t1,t2 = a.topDefPath in t2
          | _ -> failwith ("Function is not a top level definition.");
        ) in
      
      // Recursively translate arguments to simple_expr 
      // and convert returned list to array
      let av = List.to_array(List.map (fun arg -> parse arg) args) in
      
      // Finally, match the operation name with basic 
      // operator names and return according simple_expr value
      (match opname with
        | "op_Addition"    -> Add(av.(0),av.(1))
        | "op_Subtraction" -> Sub(av.(0),av.(1))
        | "op_Multiply"    -> Mul(av.(0),av.(1))
        | "op_Division"    -> Div(av.(0),av.(1))
        | _ ->
          // some other operation - error
          failwith "Not supported operation");
    | _ -> 
      // something else than efApps and efInt32 - error
      failwith "Not supported construction in expression.";;

As I mentioned, the more interesting part of the code is when the quotation matches with efApps which in our case means that the expression is mathematical operation. From the functional point of view efApps means series of function applications. The result of Query function of efApps is tuple containing expression with information about applied function (operator in our case) and parameters of this application.

For extracting operator name we match the first value with efAnyTopDef which returns information about top-level definitions. Returned structure contains assembly name as well as the function name that we need. In the second step we recursively call parse function to all arguments of operator. Once we know the function name and we have arguments translated to simple_expr, we can return the according simple_expr value.

(Cross-posted from: http://tomasp.net/blog/fsquotations.aspx)

Published Friday, July 07, 2006 2:32 AM by tomasp
Attachment(s): metacalc.zip

Comments

 

Jason Haley said:

July 7, 2006 7:41 PM
 

Don Syme's WebLog on F# and Other Research Projects said:

The F# Hub is a community site at www.hubfs.net, and&amp;nbsp;has been full of interesting posts recently.&amp;nbsp;...
July 8, 2006 6:06 PM
 

My F# Notes said:




Introduction
F# quotations allows you to easily write programs that manipulate with data representation...
October 13, 2006 4:49 PM
 

jdh30 said:

Great article. That's described the F# AST and how you can use quotations to decompose an F# expression into its AST.

I'd like to know how to do the converse: built an AST and evaluate it. For example, can you write a calculator that evaluates the expressions it is given using .NET (i.e. to native code) rather than using a slow eval function as you did here?

Cheers,
Jon.
November 17, 2006 6:17 AM
 

tomasp said:

Thanks for your comment Jon!

Currently there is no standard way (as far as I know) for compiling F# quotations (AST). Building it is not that difficult - you can either compose code quoted using <@ .. @> operator (and some other useful operators) or use ef[something].Make (see http://research.microsoft.com/projects/ilx/fsharp-manual/fslib/Microsoft.FSharp.Quotations.Raw.html for list of all quotation types that can be used).

It would be probably possible to implement compiling F# quotations by converting this "public" representation to internal AST used by F# compiler and invoking the compiler, however I'm not familiar with F# source codes enough to do this. The easier (and a little slower) option is to generate F# source code from F# quotations and compiling it using "fsc" compiler (or using my CodeDomProvider which makes it a bit easier, but internally launches "fsc" too).
November 25, 2006 6:17 PM
 

links for 2007-08-23 « dstelow notes… said:

August 23, 2007 4:49 PM
 

Dj said:

Don't Worry, Be Happy! =)









February 29, 2008 12:40 AM
 

http://cs.hubfs.net/blogs/tomasp/archive/2006/07/07/413.aspx said:

April 7, 2008 7:54 AM
 

Bart Masschelein said:

Very nice article, and it was the first article that gave me a better understanding of quotations. However, since it was also one of my first articles overall, it took me some time to figure out that this code only runs on F# version 1.1.13.8, whereas I am using the latest version, namely F# version 1.9.3.14. And things have changed in between, apparently ;-).

A bit of googling later, I encountered http://cs.hubfs.net/forums/thread/3586.aspx, where Robert answers a quotation related question. And this article, together with Roberts answer made me see the light. What I'm trying to say is that this article, although being what I was looking for, is VERY confusing. A nice compromise would be a rewrite of this article using updated coding, combined with the code generation example of Robert. The purpose of this article, which gives hints at how to create an AST, could be the more advanced example.

Mmm, maybe I should not whine, and give it a shot...

br,
Bart  
April 21, 2008 2:44 PM
 

Links on F# Quotation - TripLeZoNe Technology Playground said:

June 15, 2008 5:02 PM
 

Justin Lee's Technology Blog : Links on F# Quotation said:

June 15, 2008 5:03 PM
 

Website Directory - F said:

January 20, 2009 6:59 PM
Anonymous comments are disabled

This Blog

Post Calendar

<July 2006>
SuMoTuWeThFrSa
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

Syndication

Powered by Community Server, by Telligent Systems