Formation F#
  • Intro
  • Bases
    • Le F♯, c'est quoi ?
    • Syntaxe
    • Premiers concepts
    • 🍔 Quiz
  • Fonctions
    • Signature
    • Fonctions
    • Fonctions standard
    • Opérateurs
    • Fonctions : compléments
    • 🍔 Quiz
    • 📜 Récap’
  • Types composites
    • Généralités
    • Tuples
    • Records
    • Unions
    • Enums
    • Records anonymes
    • Types valeur
    • 🍔 Quiz
  • Types : Compléments
    • Type unit
    • Génériques
    • Types flexibles
    • Unités de mesure
    • Conversion
    • Exceptions F#
  • Pattern matching
    • Patterns
    • Match expression
    • 🚀 Active Patterns
    • 📜 Récap’
  • Collections
    • Vue d'ensemble
    • Types
    • Fonctions génériques
    • Fonctions spécifiques
    • 🍔 Quiz
    • 📜 Récap’
  • Programmation asynchrone
    • Workflow asynchrone
    • Interop avec la TPL .NET
    • 📜 Récap’
  • Types monadiques
    • Type Option
    • Type Result
    • Smart constructor
    • 🚀 Computation expression (CE)
    • 🚀 CE - Fondements théoriques
    • 📜 Récap’
  • Module & namespace
    • Vue d'ensemble
    • Namespace
    • Module
    • 🍔 Quiz
    • 📜 Récap’
  • Orienté-objet
    • Introduction
    • Membres
    • Extensions de type
    • Classe, structure
    • Interface
    • Expression objet
    • Recommandations
  • 🦚 Aller plus loin
Propulsé par GitBook
Sur cette page
  • CE : le couteau suisse ✨
  • Monoïde
  • CE monoïdale
  • Monade
  • Lois
  • Langages
  • CE monadique
  • CE monadique et générique
  • Exemple avec le type Option
  • CE monadiques spécifiques
  • Applicative
  • CE applicative
  • Applicative vs Monade
  • Autres CE
  • Expecto
  • Farmer
  • Saturn
  • Aller + loin

Cet article vous a-t-il été utile ?

Modifier sur GitHub
  1. Types monadiques

🚀 CE - Fondements théoriques

CE = Computation Expression

CE : le couteau suisse ✨

Les computation expressions servent à différentes choses :

  • C♯ yield return → F♯ seq {}

  • C♯ async/await → F♯ async {}

  • C♯ expressions LINQ from... select → F♯ query {}

  • ...

Fondements théoriques sous-jacents :

  • Monoïde

  • Monade

  • Applicative

Monoïde

≃ Type T définissant un ensemble comportant :

  1. Opération (+) : T -> T -> T

    • Pour combiner des ensembles et garder le même "type"

    • Associative : a + (b + c) ≡ (a + b) + c

  2. Élément neutre (aka identity) ≃ ensemble vide

    • Combinable à tout ensemble sans effet

    • a + e ≡ e + a ≡ a

CE monoïdale

Le builder d'une CE monoïdale (telle que seq) dispose a minima de :

  • Yield pour construire l'ensemble élément par élément

  • Combine ≡ (+) (Seq.append)

  • Zero ≡ élément neutre (Seq.empty)

S'y ajoute généralement (entre autres) :

  • For pour supporter for x in xs do ...

  • YieldFrom pour supporter yield!

Monade

≃ Type générique M<'T> comportant :

  1. Fonction return de construction

    • Signature : (value: 'T) -> M<'T>

    • ≃ Wrap une valeur

  2. Fonction bind de "liaison" (aka opérateur >>=)

    • Signature : (f: 'T -> M<'U>) -> M<'T> -> M<'U>

    • Utilise la valeur wrappée, la "map" avec la fonction f vers une valeur d'un autre type et "re-wrap" le résultat

Lois

return ≡ élément neutre pour bind

  • À gauche : return x |> bind f ≡ f x

  • À droite : m |> bind return ≡ m

bind est associatif

  • m |> bind f |> bind g ≡ m |> bind (fun x -> f x |> bind g)

Langages

Haskell

  • Monades beaucoup utilisées. Les + communes : IO, Maybe, State, Reader.

  • Monad est une classe de type pour créer facilement ses propres monades.

F♯

  • Certaines CE permettent des opérations monadiques.

  • Plus rarement utilisées directement (sauf par des Haskellers)

C♯

  • Monade implicite dans LINQ

CE monadique

Le builder d'une CE monadique dispose des méthodes Return et Bind.

Les types Option et Result sont monadiques. → On peut leur créer leur propre CE :

type OptionBuilder() =
    member _.Bind(x, f) = x |> Option.bind f
    member _.Return(x) = Some x

type ResultBuilder() =
    member _.Bind(x, f) = x |> Result.bind f
    member _.Return(x) = Ok x

CE monadique et générique

#r "nuget: FSharpPlus"
open FSharpPlus

let lazyValue = monad {
    let! a = lazy (printfn "I'm lazy"; 2)
    let! b = lazy (printfn "I'm lazy too"; 10)
    return a + b
} // System.Lazy<int>

let result = lazyValue.Value
// I'm lazy
// I'm lazy too
// val result : int = 12

Exemple avec le type Option

#r "nuget: FSharpPlus"
open FSharpPlus

let addOptions x' y' = monad {
    let! x = x'
    let! y = y'
    return x + y
}

let v1 = addOptions (Some 1) (Some 2) // Some 3
let v2 = addOptions (Some 1) None     // None
#r "nuget: FSharpPlus"
open FSharpPlus

let v1 = monad {
    let! a = Ok 2
    let! b = Some 10
    return a + b
} // 💥 Error FS0043...

let v2 = monad {
    let! a = Ok 2
    let! b = Some 10 |> Option.toResult
    return a + b
} // val v2 : Result<int,unit> = Ok 12

CE monadiques spécifiques

☝ Recommandé car + explicite que CE monad

#r "nuget: FSToolkit.ErrorHandling"
open FsToolkit.ErrorHandling

let addOptions x' y' = option {
    let! x = x'
    let! y = y'
    return x + y
}

let v1 = addOptions (Some 1) (Some 2) // Some 3
let v2 = addOptions (Some 1) None     // None

Applicative

A.k.a Applicative Functor

≃ Type générique M<'T>

3 "styles" :

Style B : Applicatives avec mapN • map2, map3... map5 combine 2 à 5 valeurs wrappées

☝ Conseil : Styles B et C sont autant recommandés l'un que l'autre.

CE applicative

Permet d'accumuler les erreurs → Usages : • Parsing d'inputs externes • Smart constructor (Exemple de code slide suivante...)

#r "nuget: FSToolkit.ErrorHandling"
open FsToolkit.ErrorHandling

type [<Measure>] cm
type Customer = { Name: string; Height: int<cm> }

let validateHeight height =
    if height <= 0<cm>
    then Error "Height must me positive"
    else Ok height

let validateName name =
    if System.String.IsNullOrWhiteSpace name
    then Error "Name can't be empty"
    else Ok name

module Customer =
    let tryCreate name height : Result<Customer, string list> =
        validation {
            let! validName = validateName name
            and! validHeight = validateHeight height
            return { Name = validName; Height = validHeight }
        }

let c1 = Customer.tryCreate "Bob" 180<cm>  // Ok { Name = "Bob"; Height = 180 }
let c2 = Customer.tryCreate "Bob" 0<cm> // Error ["Height must me positive"]
let c3 = Customer.tryCreate "" 0<cm>    // Error ["Name can't be empty"; "Height must me positive"]

Applicative vs Monade

Soit N opérations tryXxx renvoyant un Option ou Result

Style monadique :

  • Avec bind ou CE let! ... let! ...

  • Chaîne les opérations, exécutée 1 à 1, la N dépendant de la N-1

  • S'arrête à 1ère opération KO → juste 1ère erreur dans Result ①

module Result =
    // f : 'T -> Result<'U, 'Err>
    // x': Result<'T, 'Err>
    //  -> Result<'U, 'Err>
    let bind f x' =
        match x' with
        | Error e  -> Error e // 👈 (1)
        | Ok value -> f value

Style applicatif :

  • Avec mapN ou CE let! ... and! ...

  • Combine 2..N opérations indépendantes → parallélisables 👍

  • Permet de combiner les cas Error contenant une List ②

module Validation =
    // f : 'T -> 'U -> Result<'V, 'Err list>
    // x': Result<'T, 'Err list>
    // y': Result<'U, 'Err list>
    //  -> Result<'V, 'Err list>
    let map2 f x' y' =
        match x', y' with
        | Ok x, Ok y -> f x y
        | Ok _, Error errors | Error errors, Ok _ -> Error errors
        | Error errors1, Error errors2 -> Error (errors1 @ errors2) // 👈 ②

Autres CE

On a vu 2 librairies qui étendent F♯ et proposent leurs CE :

  • FSharpPlus → monad

  • FsToolkit.ErrorHandling → option, result, validation

Beaucoup de librairies ont leur propre DSL (Domain Specific Language.) Certaines s'appuient alors sur des CE :

  • Expecto

  • Farmer

  • Saturn

Expecto

❝ Librairie de testing : assertions + runner ❞ 🔗 https://github.com/haf/expecto

open Expecto

let tests =
  test "A simple test" {
    let subject = "Hello World"
    Expect.equal subject "Hello World" "The strings should equal"
  }

[<EntryPoint>]
let main args =
  runTestsWithCLIArgs [] args tests

Farmer

❝ Infrastructure-as-code pour Azure ❞

// Create a storage account with a container
let myStorageAccount = storageAccount {
    name "myTestStorage"
    add_public_container "myContainer"
}

// Create a web app with application insights that's connected to the storage account
let myWebApp = webApp {
    name "myTestWebApp"
    setting "storageKey" myStorageAccount.Key
}

// Create an ARM template (Azure Resource Manager)
let deployment = arm {
    location Location.NorthEurope
    add_resources [
        myStorageAccount
        myWebApp
    ]
}

// Deploy it to Azure!
deployment
|> Writer.quickDeploy "myResourceGroup" Deploy.NoParameters

Saturn

❝ Framework Web au-dessus de ASP.NET Core, pattern MVC ❞

open Saturn
open Giraffe

let app = application {
    use_router (text "Hello World from Saturn")
}

run app

Aller + loin

Précédent🚀 Computation expression (CE)Suivant📜 Récap’

Dernière mise à jour il y a 2 ans

Cet article vous a-t-il été utile ?

Librairie de programmation fonctionnelle

propose une CE monad → Marche pour tous les types monadiques : Option, Result, ... et même Lazy !

Limite : on ne peut pas mélanger plusieurs types monadiques !

Librairie propose : • CE option {} spécifique au type Option<'T> (exemple ci-dessous) • CE result {} spécifique au type Result<'Ok, 'Err>

Style A : Applicatives avec apply/<*> et pure/return • ❌ Pas facile à comprendre • 💡 Présenté par Jérémie Chassaing dans le talk ❝❞ • ☝ Déconseillé par Don Syme dans cette

Style C : Applicatives avec let! ... and! ... dans une CE • Même principe : combiner plusieurs valeurs wrappées • Disponible à partir de F♯ 5 ()

Librairie propose : • Type Validation<'Ok, 'Err> ≡ Result<'Ok, 'Err list> • CE validation {} supportant syntaxe let!...and!...

de Scott Wlaschin

🔗

🔗

📹 - 📜

🔗

⚠️
LanguageExt
FSharpPlus
FsToolkit.ErrorHandling
Applicatives in real life
note de nov. 2020
annonce de nov. 2020
FsToolkit.ErrorHandling
Railway-oriented programming
github.com/compositionalit/farmer
saturnframework.org
Extending F# through Computation Expressions
Slides
Computation Expressions Workshop