F# Training
Formation F#
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
Powered by GitBook
On this page
  • Présentation
  • Module Result
  • Quiz 🎲
  • Solution
  • Result : tracks Success/Failure
  • Result vs Option
  • Result vs Validation

Was this helpful?

Edit on GitHub
  1. Types monadiques

Type Result

Présentation

A.k.a Either (Haskell)

Modélise une double-track Succès/Échec

type Result<'Success, 'Error> =   // ☝ 2 paramètres génériques
    | Ok of 'Success    // Track "Succès" == Happy path
    | Error of 'Error   // Track "Échec"  == Voie de garage

Gestion fonctionnelle des erreurs métier (les erreurs prévisibles)

  • Permet de limiter usage des exceptions aux erreurs exceptionnelles

  • Dès qu'une opération échoue, les opérations restantes ne sont pas lancées

  • Railway-oriented programming, F# for Fun and Profit

Module Result

Ne contient que 3 fonctions (en F# 5.0) :

map f option : sert à mapper le résultat

  • ('T -> 'U) -> Result<'T, 'Error> -> Result<'U, 'Error>

mapError f option : sert à mapper l'erreur

  • ('Err1 -> 'Err2) -> Result<'T, 'Err1> -> Result<'T, 'Err2>

bind f option : idem map avec fonction f qui renvoie un Result

  • ('T -> Result<'U, 'Error>) -> Result<'T, 'Error> -> Result<'U, 'Error>

  • 💡 Le résultat est aplati, comme la fonction flatMap sur les arrays JS

💡 Depuis F# 7.0, le module Result contient plus de fonctions afin d'être aussi fonctionnel que le type Option , avec les correspondances suivantes :

Option
Result
List

Some value

Ok value

[ value ]

None

Error () / Error _

[ ]

isSome / isNone

isOk / isError

× / isEmpty

contains someValue

contains okValue

contains value

count

count

length

defaultValue value defaultWith defThunk

defaultValue value defaultWith defThunk

exists predicate forall predicate

exists predicate forall predicate

exists predicate forall predicate

filter predicate

×

filter predicate

iter action map f / bind f fold f state

iter action map f / bind f fold f state

iter action map f / bind f fold f state

×

mapError f

×

toArray / toList

toArray / toList

×

×

toOption

×

Quiz 🎲

Implémenter Result.map et Result.bind

💡 Tips :

  • Mapping sur la track Succès

  • Accès à la valeur dans la track Succès :

    • Utiliser pattern matching (match result with...)

  • Retour : simple Result, pas un Result<Result> !

Solution

// ('T -> 'U) -> Result<'T, 'Error> -> Result<'U, 'Error>
let map f result =
    match result with
    | Ok x    -> Ok (f x)  // ☝ Ok -> Ok
    | Error e -> Error e   // ⚠️ Les 2 `Error e` n'ont pas le même type !

// ('T -> Result<'U, 'Error>) -> Result<'T, 'Error>
//                            -> Result<'U, 'Error>
let bind f result =
    match result with
    | Ok x    -> f x       // ☝ Ok -> Ok ou Error !
    | Error e -> Error e

Result : tracks Success/Failure

map : pas de changement de track

Track      Input          Operation      Output
Success ─ Ok x    ───► map( x -> y ) ───► Ok y
Failure ─ Error e ───► map(  ....  ) ───► Error e

bind : routage possible vers track Failure mais jamais l'inverse

Track     Input              Operation           Output
Success ─ Ok x    ─┬─► bind( x -> Ok y     ) ───► Ok y
                   └─► bind( x -> Error e2 ) ─┐
Failure ─ Error e ───► bind(     ....      ) ─┴─► Error ~

☝ Opération de mapping/binding jamais exécutée dans track Failure

Result vs Option

Option peut représenter le résultat d'une opération qui peut échouer ☝ Mais en cas d'échec, l'option ne contient pas l'erreur, juste None

Option<'T> ≃ Result<'T, unit>

  • Some x ≃ Ok x

  • None ≃ Error ()

  • Cf. fonctions Option.toResult et Option.toResultWith error de FSharpPlus

let toResultWith (error: 'Error) (option: 'T option) : Result<'T, 'Error> =
    match option with
    | Some x -> Ok x
    | None   -> Error error

Exemple :

Modification de la fonction checkAnswer précédente pour indiquer l'erreur :

open FSharpPlus

type Answer = A | B | C | D
type Error = InvalidInput | WrongAnswer

let tryParseAnswer text = ... // string -> Answer option

let checkAnswer (expectedAnswer: Answer) (givenAnswer: string) =
    let check answer = if answer = expectedAnswer then Ok answer else Error WrongAnswer
    tryParseAnswer givenAnswer           // Answer option
    |> Option.toResultWith InvalidInput  // Result<Answer, Error>
    |> Result.bind check
    |> function
       | Ok _               -> "✅"
       | Error InvalidInput -> "❌ Invalid Input"
       | Error WrongAnswer  -> "❌ Wrong Answer"

["X"; "A"; "B"] |> List.map (checkAnswer B)  // ["❌ Invalid Input"; "❌ Wrong Answer"; "✅"]

Result vs Validation

Result est "monadique" : à la 1ère erreur, on "débranche"

Validation est "applicatif" : permet d'accumuler les erreurs

  • ≃ Result<'ok, 'error list>

  • Pratique pour valider saisie utilisateur et remonter ∑ erreurs

  • Dispo dans librairies FSharpPlus, FsToolkit.ErrorHandling

Plus d'info : 🔗 Validation with F# 5 and FsToolkit, Compositional IT, Dec 2020

PreviousType OptionNextSmart constructor

Last updated 2 years ago

Was this helpful?

Même type d'erreur 'Error pour f et le result en entrée

⚠️