Table of Contents

F# techniques

F# users can enjoy a superb serialization experience with Nerdbank.MessagePack. Nerdbank.MessagePack's union support includes support for native F# union types, thanks for PolyType's built-in support for them.

The following snippet shows serializing a farm with various animals, converting to JSON for inspection, and deserializing the msgpack back again.

type Animal =
    | Cow of name: string * weight: int
    | Horse of name: string * speed: int
    | Dog of name: string * color: string

type Farm = { Animals: Animal list }

let farm = {
    Animals = [
        Cow("Bessie", 1500)
        Horse("Spirit", 45)
        Dog("Rex", "Brown") 
    ] 
}

let serializer = MessagePackSerializer()
let msgpack =
    let refableValue = farm // need to pass by reference
    serializer.Serialize(&refableValue, ReflectionTypeShapeProvider.Default)

MessagePackSerializer.ConvertToJson(msgpack) |> printfn "Farm as JSON: %s"

let newFarm = serializer.Deserialize<Farm>(msgpack, ReflectionTypeShapeProvider.Default)

printfn "Farm animals:"
newFarm.Animals |> Seq.iter (function
    | Cow(name, weight) -> printfn "Cow: %s, Weight: %d" name weight
    | Horse(name, speed) -> printfn "Horse: %s, Speed: %d" name speed
    | Dog(name, color) -> printfn "Dog: %s, Color: %s" name color)

AOT readiness

The above snippet uses the ReflectionTypeShapeProvider which allows a single F# project to work out of the box.

Reflection can be avoided, and an F# program can be AOT-safe by:

  1. Define your data layer in an F# library project.
  2. Define a witness type for your F# data types from within a C# project that references your F# data types library.
  3. Define your F# application that references both your data layer F# project and your C# witness type project. Pass the witness type to any serialize/deserialize method such as Serialize<T, TProvider>(in T?, CancellationToken) and Deserialize<T, TProvider>(ReadOnlyMemory<byte>, CancellationToken)