Recherche…
Définition de l'option
Une Option est une union discriminée avec deux cas, None ou Some .
type Option<'T> = Some of 'T | None
Utilisez Option <'T> sur des valeurs nulles
Dans les langages de programmation fonctionnels comme null valeurs null F# sont considérés comme potentiellement nuisibles et de style médiocre (non idiomatique).
Considérez ce code C# :
string x = SomeFunction ();
int l = x.Length;
x.Length lancera si x est null ajoutons une protection:
string x = SomeFunction ();
int l = x != null ? x.Length : 0;
Ou:
string x = SomeFunction () ?? "";
int l = x.Length;
Ou:
string x = SomeFunction ();
int l = x?.Length;
Dans les idiomatiques F# null valeurs null ne sont pas utilisées, notre code ressemble à ceci:
let x = SomeFunction ()
let l = x.Length
Cependant, il est parfois nécessaire de représenter des valeurs vides ou invalides. Ensuite, nous pouvons utiliser Option<'T> :
let SomeFunction () : string option = ...
SomeFunction soit retourne Some string valeur ou None . Nous extrayons la valeur de string utilisant une correspondance de modèle
let v =
match SomeFunction () with
| Some x -> x.Length
| None -> 0
La raison pour laquelle ce code est moins fragile que:
string x = SomeFunction ();
int l = x.Length;
C'est parce que nous ne pouvons pas appeler Length sur une string option . Nous devons extraire la valeur de string utilisant la correspondance de modèle et, ce faisant, nous sommes sûrs que la valeur de string est sûre à utiliser.
Le module d'option permet la programmation orientée chemin de fer
La gestion des erreurs est importante mais peut rendre un algorithme élégant en désordre. La programmation orientée chemin de fer ( ROP ) est utilisée pour rendre la gestion des erreurs élégante et composable.
Considérons la fonction simple f :
let tryParse s =
let b, v = System.Int32.TryParse s
if b then Some v else None
let f (g : string option) : float option =
match g with
| None -> None
| Some s ->
match tryParse s with // Parses string to int
| None -> None
| Some v when v < 0 -> None // Checks that int is greater than 0
| Some v -> v |> float |> Some // Maps int to float
Le but de f est d'analyser l'entrée string valeur (s'il y a Some ) en un int . Si l' int est supérieur à 0 on le transforme en float . Dans tous les autres cas, nous renflouons avec None .
Bien que la fonction imbriquée soit extrêmement simple, la match imbriquée diminue considérablement la lisibilité.
ROP observe que nous avons deux types de chemins d'exécution dans notre programme
- Happy path - calculera éventuellement une
Somevaleur - Chemin d'erreur - Tous les autres chemins produisent
None
Comme les chemins d’erreur sont plus fréquents, ils ont tendance à prendre le relais. Nous aimerions que le code de chemin heureux soit le chemin de code le plus visible.
Une fonction équivalente g utilisant ROP pourrait ressembler à ceci:
let g (v : string option) : float option =
v
|> Option.bind tryParse // Parses string to int
|> Option.filter ((<) 0) // Checks that int is greater than 0
|> Option.map float // Maps int to float
Cela ressemble beaucoup à la façon dont nous avons tendance à traiter les listes et les séquences en F# .
On peut voir une Option<'T> comme une List<'T> qui ne peut contenir que 0 ou 1 élément où Option.bind se comporte comme List.pick (conceptuellement, Option.bind mieux List.collect à List.collect mais List.pick peut être plus facile à comprendre).
bind , filter et map gère les chemins d’erreur et g ne contient que le code de chemin heureux.
Toutes les fonctions qui acceptent directement l' Option<_> et renvoient l' Option<_> sont directement composables avec |> et >> .
ROP augmente donc la lisibilité et la composabilité.
Utiliser les types d'option de C #
Ce n'est pas une bonne idée d'exposer les types d'options au code C #, car C # ne permet pas de les gérer. Les options sont soit d'introduire FSharp.Core comme une dépendance dans votre projet C # (ce que vous devez faire si vous utilisez une bibliothèque F # non conçue pour l'interopérabilité avec C #), soit de changer les valeurs None en null .
Pré-F # 4.0
Pour ce faire, créez votre propre fonction de conversion:
let OptionToObject opt =
match opt with
| Some x -> x
| None -> null
Pour les types de valeur, vous devez avoir recours à la boxe ou à System.Nullable .
let OptionToNullable x =
match x with
| Some i -> System.Nullable i
| None -> System.Nullable ()
F # 4.0
Dans F # 4.0, les fonctions ofObj , toObj , ofNullable et toNullable introduites dans le module Option . Dans F # interactive, ils peuvent être utilisés comme suit:
let l1 = [ Some 1 ; None ; Some 2]
let l2 = l1 |> List.map Option.toNullable;;
// val l1 : int option list = [Some 1; null; Some 2]
// val l2 : System.Nullable<int> list = [1; null; 2]
let l3 = l2 |> List.map Option.ofNullable;;
// val l3 : int option list = [Some 1; null; Some 2]
// Equality
l1 = l2 // fails to compile: different types
l1 = l3 // true
Notez que None compile en null interne. Cependant, en ce qui concerne F #, il s'agit d'un None .
let lA = [Some "a"; None; Some "b"]
let lB = lA |> List.map Option.toObj
// val lA : string option list = [Some "a"; null; Some "b"]
// val lB : string list = ["a"; null; "b"]
let lC = lB |> List.map Option.ofObj
// val lC : string option list = [Some "a"; null; Some "b"]
// Equality
lA = lB // fails to compile: different types
lA = lC // true