A code quotation turns a piece of F# code into a value that describes the code, an expression tree, instead of running it. Quotations are used by query translation, by tools that compile F# to other targets (GPUs, SQL, JavaScript) and by libraries that analyse code.
The quotation marks
| Form | Type | Notes |
|---|---|---|
<@ expr @> | Expr<'T> | a typed quotation: the type of the quoted expression is kept |
<@@ expr @@> | Expr | an untyped quotation: faster to traverse, without the type parameter |
%e | splices a typed Expr<'T> value into a typed quotation | |
%%e | splices an untyped Expr into a quotation | |
[<ReflectedDefinition>] | on a function or member: its body is stored as a quotation that Expr.TryGetReflectedDefinition and the pattern (|MethodWithReflectedDefinition|_|) can read |
<@ and @> must be separated from what is inside by a space only when that avoids reading them as part of an operator (<@ 1 + 2 @>). A list or array that begins with a quotation needs a space after the bracket ([ <@ 1 @> ]), because [< starts an attribute.
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
let q : Expr<int> = <@ 1 + 2 * 3 @>
let u : Expr = <@@ "text".Length @@>
let x = 10
let spliced = <@ %q + x @> // the value of x is captured; q is spliced in
let rec describe (e: Expr) =
match e with
| Value (v, _) -> sprintf "const %A" v
| Call (None, mi, args) -> sprintf "%s(%s)" mi.Name (args |> List.map describe |> String.concat ", ")
| Lambda (v, body) -> sprintf "fun %s -> %s" v.Name (describe body)
| _ -> "?"
printfn "%s" (describe q.Raw)
let result = Linq.RuntimeHelpers.LeafExpressionConverter.EvaluateQuotation q // evaluate
The Expr type and its shapes
An Expr is built by a quotation or by the static methods of the Expr class. The methods of FSharp.Quotations.Expr in the library interface are: AddressOf, AddressSet, Application, Applications, Call, CallWithWitnesses, Cast, Coerce, DefaultValue, Deserialize, FieldGet, FieldSet, ForIntegerRangeLoop, IfThenElse, Lambda, Let, LetRecursive, NewArray, NewDelegate, NewObject, NewRecord, NewStructTuple, NewTuple, NewUnionCase, PropertyGet, PropertySet, Quote, QuoteRaw, QuoteTyped, RegisterReflectedDefinitions, Sequential, Substitute, TryFinally, TryWith, TupleGet, TypeTest, UnionCaseTest, Value, ValueWithName, Var, VarSet, WhileLoop, WithValue, and the property and helper methods CustomAttributes, GetFreeVars, Type and TryGetReflectedDefinition.
The module Patterns has an active pattern for each of these shapes; DerivedPatterns adds the shapes that are built from them; ExprShape has one pattern that splits any expression into three shapes.
| Module | Active patterns (from the library interface) |
|---|---|
Patterns | AddressOf AddressSet Application Call CallWithWitnesses Coerce DefaultValue FieldGet FieldSet ForIntegerRangeLoop IfThenElse Lambda Let LetRecursive NewArray NewDelegate NewObject NewRecord NewStructTuple NewTuple NewUnionCase PropertyGet PropertySet Quote QuoteRaw QuoteTyped Sequential TryFinally TryWith TupleGet TypeTest UnionCaseTest Value ValueWithName Var VarSet WhileLoop WithValue |
DerivedPatterns | AndAlso OrElse Bool Byte SByte Char Decimal Double Single String Unit Lambdas Applications SpecificCall MethodWithReflectedDefinition PropertyGetterWithReflectedDefinition PropertySetterWithReflectedDefinition Int16 Int32 Int64 UInt16 UInt32 UInt64 |
ExprShape | (|ShapeVar|ShapeLambda|ShapeCombination|): a variable, a lambda, or any other node with its sub-expressions (rebuild with RebuildShapeCombination) |
Quotations in the language
- A quotation may contain any expression the compiler can express as a tree; a few forms (some object-expression and byref uses) are not allowed. A quotation captures the value of a local variable from outside it, and a variable that is bound inside it stays a
Var. - Quotations are how
query { … }is translated: the computation expression builder can request its body as a quotation through the builder’sQuotemethod (see computation expressions). - F# 5 improved quotations (the documentation lists the change under “F# quotations improvements”).
- Quotation symbols are read by the lexer as the tokens
<@,<@@,@>,@@>(and@>.and@>|}as shapes next to a dot or an anonymous-record end); the viewer colours them as directives.
Not described
Known, not yet covered
- Reference: the semantics of each Expr method (arguments and results), what a quotation of a particular construct looks like,
ReflectedDefinitionrules, the cases that cannot be quoted, serialisation (Deserialize), and the F# 5 improvements are only named. The lists of methods and patterns were taken from the names inquotations.fsi; the split betweenPatternsandDerivedPatternswas read from the line ranges of the two modules in that file. - Reference: the example code was written for this page and not compiled.
See also
- Pattern matching and active patterns – the patterns used to walk a tree
- Lexical structure – the quotation tokens