Syntax: same as Record with "fat" braces {| fields |}
{| Age: int |} ā signature
{| Age = 15 |} ā instance
Inline typing: no need to pre-define a named type
Alternative to Tuples
Allowed in function input/output
ā Anonymous type CāÆ
Benefits ā
Reduce boilerplate
Improve interop with external systems (JavaScript, SQL...)
Compatible with C⯠anonymous types:
ā When a C⯠API requires an anonymous type, we can give it an anonymous record instance.
Examples (more on this later) :
LINQ projection
Customization of an existing record
JSON serialization
Inline signature
Alias by module
LINQ Projection
š” Select a subset of properties
let names =
query {
for p in persons do
select {| Name = p.FirstName |}
}
In CāÆ, we would use an anonymous type:
var names =
from p in persons
select new { Name = p.FirstName };
Customize an existing record
š” An anonymous record can be instantiated from a record instance
type Person = { Age: int; Name: string }
let william = { Age = 12; Name = "William" }
// Add a field (Gender)
let william' = {| william with Gender = "Male" |}
// {| Age = 12; Name = "William"; Gender = "Male" |}
// Modify fields (Name, Age: int => float)
let jack = {| william' with Name = "Jack"; Age = 16.5 |}
// {| Age = 16.5; Name = "Jack"; Gender = "Male" |}
JSON serialization
š” An anonymous record can be used as an intermediary type to serialize a union in JSON.
Example:
#r "nuget: Newtonsoft.Json"
let serialize obj = Newtonsoft.Json.JsonConvert.SerializeObject obj
type CustomerId = CustomerId of int
type Customer = { Id: CustomerId; Age: int; Name: string; Title: string option }
serialize { Id = CustomerId 1; Age = 23; Name = "Abc"; Title = Some "Mr" }
š” Solution: Define an anonymous record as "DTO" to serialize a customer.
let serialisable customer =
let (CustomerId customerId) = customer.Id
{| customer with
Id = customerId
Title = customer.Title |> Option.toObj |}
serialize (serialisable { Id = CustomerId 1; Age = 23; Name = "Abc"; Title = Some "Mr" })
š” Use an anonymous record declared inside a bigger type to reduce cognitive load:
type Title = Mr | Mrs
type Customer =
{ Age : int
Name : {| First: string; Middle: string option; Last: string |} // š
Title: Title option }
Limits š
// No inference from field usage
let nameKo x = x.Name // š„ Error FS0072: Lookup on object of indeterminate type...
let nameOk (x: {| Name:string |}) = x.Name
// No deconstruction
let x = {| Age = 42 |}
let { Age = age } = x // š„ Error FS0039: The record label 'Age' is not defined
let {| Age = age |} = x // š„ Error FS0010: Unexpected symbol '{|' in let binding
// No full objects merge
let banana = {| Fruit = "Banana" |}
let yellow = {| Color = "Yellow" |}
let banYelKo = {| banana with yellow |} // š„ Error FS0609...
let banYelOk = {| banana with Color = "Yellow" |}
// No omissions
let ko = {| banYelOk without Color |} // š„ No 'without' keyword