Type Option

Présentation

A.k.a Maybe (Haskell), Optional (Java 8)

Modélise l'absence de valeur → Dans le sens d'une possibilité pour une valeur d'être absente → Différent de unit qui est utilisé dans le cas où il n'y a jamais de valeur

Défini sous la forme d'une union avec 2 cases :

type Option<'Value> =
    | None              // Case sans donnée → quand valeur absente
    | Some of 'Value    // Case avec donnée → quand valeur présente

Cas d'utilisation

Modéliser un champ optionnel

type Civility = Mr | Mrs
type User = { Name: string; Civility: Civility option }

let joey  = { Name = "Joey"; Civility = Some Mr }
let guest = { Name = "Guest"; Civility = None }

→ Rend explicite le fait que Name est obligatoire et Civility facultatif

Attention : ce design n'empêche pas ici d'avoir Name = null (limite BCL)

Opération partielle

Opération où aucune valeur de sortie n'est possible pour certaines entrées.

Exemples

Exemple 1 - Inverse d'un nombre

Fonction

Opération

Signature

n = 0.5

n = 0.0

inverse

Partielle

float -> float

2.0

infinity

tryInverse

Totale

float -> float option

Some 2.0

None 👌

Exemple 2 - Recherche d'un élément dans une collection

  • Opération partielle : find predicate → 💥 quand élément non trouvé

  • Opération totale : tryFind predicateNone ou Some item

Avantages 👍

  • Explicite (honnête) concernant la partialité de l'opération

    • Pas de valeur spéciale (et cachée) : null, infinity

    • Pas d'exception

  • Force le code appelant à gérer la totalité des cas :

    • Présence d'une valeur en sortie : Some value

    • Absence d'une valeur en sortie : None

Flux de contrôle

Pour tester la présence de la valeur (de type 'T) dans l'option

  • ❌ Ne pas utiliser IsSome, IsNone et Value (🤞💥)

    • if option.IsSome then option.Value...

  • 👌 A la main avec pattern matching

  • ✅ Fonctions du module Option

Manuel avec pattern matching

Exemple :

Intégré au module Option

Opération de Mapping de la valeur (de type 'T) si ∃ :

  • option |> Option.map f avec f opération totale 'T -> 'U

  • option |> Option.bind f avec f opération partielle 'T -> 'U option

Conserver la valeur si ∃ et si respecte condition :

  • option |> Option.filter predicate avec predicate: 'T -> bool appelé que si valeur ∃

Exercice

Implémenter map, bind et filter avec pattern matching

Solution

Réponses 🎁

Exemple

Bénéfices

Rend logique métier + lisible

  • Pas de if hasValue then / else

  • Met en valeur le happy path

  • Centralise à la fin la gestion de l'absence de valeur

💡 Les 🚀 Computation expression (CE)📍 fournissent une syntaxe alternative + légère

Option vs List

Option ≃ Liste de 0 ou 1 élément → cf. fonction Option.toList

☝ Une List peut avoir + de 1 élément → Type Option modélise mieux l'absence de valeur que type List

💡 Module Option : beaucoup de même fonctions que module Listcontains, count, exist, filter, fold, forall, map

Option vs null

De part ses interactions avec la BCL, F♯ autorise parfois la valeur null

👉 Bonne pratique → Isoler ces cas de figure et wrapper dans un type Option → Par exemple en utilisant la fonction Option.ofObj

Option vs Nullable

Type System.Nullable<'T>Option<'T> en + limité

  • ❗ Ne marche pas pour les types références

  • ❗ Manque comportement monadique i.e. fonctions map et bind

  • ❗ En F♯, pas de magie comme en C♯ / mot clé null

👉 Option est le type idiomatique en F♯

💡 On utilise le type Nullable en cas d'interop. Pour l'instancier : → ❌ Contrairement au C♯, le mot clé null ne marche pas ! → 👎 L'emploie de Unchecked.defaultof marche mais n'est pas élégant. → 👍 Utiliser l'un des constructeurs : Nullable() ou Nullable(value)

Mis à jour

Ce contenu vous a-t-il été utile ?