Quotations, Reflection and Metaprogramming

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

// Quotations: code as data (typed ASTs)
let expr: Expr<int> = <@ 1 + 2 * 3 @>
let untyped = <@@ fun x -> x + 1 @@>

let rec describe (e: Expr) =
    match e with
    | Value (v, _) -> string v
    | Call (_, mi, [ a; b ]) -> $"({describe a} {mi.Name} {describe b})"
    | Lambda (v, body) -> $"fun {v.Name} -> {describe body}"
    | Var v -> v.Name
    | _ -> "?"

describe expr       // e.g. "(1 op_Addition (2 op_Multiply 3))"

// Evaluate quotations with FSharp.Quotations.Evaluator or LINQ expression conversion
// Quotations power: Fable (historically), query translation (SQL), GPU compilers, test assertions (Unquote: test <@ x = 2 @>)

// [<ReflectedDefinition>] keeps a function's quotation available at runtime
[<ReflectedDefinition>]
let square x = x * x

// Reflection
open Microsoft.FSharp.Reflection
type Shape = Circle of float | Square of float
FSharpType.GetUnionCases typeof<Shape> |> Array.map _.Name     // [|"Circle"; "Square"|]
let case, fields = FSharpValue.GetUnionFields(Circle 2.0, typeof<Shape>)
type Pt = { X: int; Y: int }
FSharpType.GetRecordFields typeof<Pt> |> Array.map _.Name      // [|"X"
…