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
Some
valeur - 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