F# Training
F# Training
F# Training
  • Presentation
  • Fundamentals
    • Introduction
    • Syntax
      • Bases
      • Functions
      • Rules
      • Exceptions
    • First concepts
    • šŸ”Quiz
  • Functions
    • Signature
    • Concepts
    • Syntax
    • Standard functions
    • Operators
    • Addendum
    • šŸ”Quiz
    • šŸ“œSummary
  • Types
    • Overview
    • Tuples
    • Records
    • Unions
    • Enums
    • Anonymous records
    • Value types
    • šŸ“œRecap
    • Addendum
  • Monadic types
    • Intro
    • Option type
    • Result type
    • Smart constructor
    • šŸš€Computation expression (CE)
    • šŸš€CE theoretical basements
    • šŸ“œRecap
  • Pattern matching
    • Patterns
    • Match expression
    • Active patterns
    • šŸš€Fold function
    • šŸ“œRecap
    • šŸ•¹ļøExercises
  • Collections
    • Overview
    • Types
    • Common functions
    • Dedicated functions
    • šŸ”Quiz
    • šŸ“œRecap
  • Asynchronous programming
    • Asynchronous workflow
    • Interop with .NET TPL
    • šŸ“œRecap
  • Module and Namespace
    • Overview
    • Namespace
    • Module
    • šŸ”Quiz
    • šŸ“œRecap
  • Object-oriented
    • Introduction
    • Members
    • Type extensions
    • Class, Struct
    • Interface
    • Object expression
    • Recommendations
Powered by GitBook
On this page
  • Introduction
  • Benefits āœ…
  • LINQ Projection
  • Customize an existing record
  • JSON serialization
  • Signature inline
  • Limits šŸ›‘

Was this helpful?

Edit on GitHub
  1. Types

Anonymous records

Introduction

  • Since F♯ 4.6 (March 2019)

  • 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" }

Resulting JSON:

{
  "Id": { "Case": "CustomerId", "Fields": [ 1 ] }, // šŸ‘€
  "Age": 23,
  "Name": "Abc",
  "Title": { "Case": "Some", "Fields": [ "Mr" ] }  // šŸ‘€
}

→ Format verbose and not practical.

šŸ’” 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" })

Resulting JSON:

{
  "Id": 1, // āœ…
  "Age": 23,
  "Name": "Abc",
  "Title": "Mr"  // āœ…
}

Signature inline

šŸ’” 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
PreviousEnumsNextValue types

Last updated 21 days ago

Was this helpful?

šŸ”— by Krzysztof Kraszewski

F# vs C#: Anonymous Records