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
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 :
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 unResult<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
Result
: tracks Success/Failuremap
: 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
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
etOption.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
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
Last updated
Was this helpful?