In my investigations into type providers, I started digging into a feature of F# called quotations. These blur the boundary between code and data; a representation of an expression tree that you can then evaluate or manipulate.
Why is this useful? Well; it's used in a number of places in various F# libraries. As mentioned above, type providers use them as a mechanism for providing the invocation code for the types that are being provided. The compiler can then take that expression tree and turn in into clr code.
They can also be useful as a way of defining code within your F# that can then be translated into other programming languages. The linq to sql implementation does this (turning your linq into SQL, fairly obviously!) while the FunScript project compiles your F# quotations into JavaScript.
So; linked features, often used in concert: quotations allow you to generate expressions at runtime, manipulate them at run time and evaluate them at run time - where evaluation covers everything from running the code on the clr to outputting it as a different language.
Creating expressions is fairly straightforward. If you have a valid F# expression, you can wrap it in <@ ... @>
(or <@@ ... @@>
, see below…):
1 2 3 4 5 6 7 |
|
What's the difference between the two? Well, the first with it's strong typing provides you with greater safety if you know what types you're expecting an expression tree to evaluate to - but those same type restraints prevent you from writing methods which can transform and return expressions whose types are unknown at compile time. There are also, apparently, some performance implications to carrying around the type information.
You can also generate the expression trees directly using the Expr
module in the Microsoft.FSharp.Quotations
namespace.
1 2 3 4 5 6 |
|
The above being identical to: <@@ System.Math.Cos(1.0) @@>
. Building directly with the classes becomes especially useful when doing things like recursively building expression trees; the F# compilers type inference tends to get a little unhappy trying to infer the types of the quotations and the expressions you're splicing into them on occasion.
Splicing?
Okay, so I slightly snuck that one in there. If you're building expressions with the Expr
module it's obviously how you could create functions that could compose into larger expression trees. But the F# quotation syntax also allows you to do something similar, splicing values in with the %
and %%
operators.
An example is worth 1,000 words in these situations:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
As an aside, the generic Expr
type has the Raw
property which exposes the untyped version of the quotation. Which, as quotations have value based equality, allows us to do this:
1 2 3 |
|
And of course we can build up more complex trees if we wish:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Finished here? Time to check out part 2 about how to manipulate quotations once you have them: Cutting Quotations Down to Size.
Yes, I know the title quote is inaccurate - but I'm afraid I prefer it this way.