Ricerca…
Definizione dell'opzione
Option
è un'unione discriminata con due casi, None
o Some
.
type Option<'T> = Some of 'T | None
Usa l'opzione <'T> su valori nulli
Nei linguaggi di programmazione funzionale come F#
null
valori null
sono considerati potenzialmente dannosi e di stile scadente (non idiomatici).
Considera questo codice C#
:
string x = SomeFunction ();
int l = x.Length;
x.Length
getterà se x
è null
aggiungiamo protezione:
string x = SomeFunction ();
int l = x != null ? x.Length : 0;
O:
string x = SomeFunction () ?? "";
int l = x.Length;
O:
string x = SomeFunction ();
int l = x?.Length;
In F#
idiomatic null
valori null
non sono usati, quindi il nostro codice assomiglia a questo:
let x = SomeFunction ()
let l = x.Length
Tuttavia, a volte è necessario rappresentare valori vuoti o non validi. Quindi possiamo usare Option<'T>
:
let SomeFunction () : string option = ...
SomeFunction
restituisce Some
valore di string
o None
. Estraiamo il valore della string
utilizzando la corrispondenza del modello
let v =
match SomeFunction () with
| Some x -> x.Length
| None -> 0
Il motivo per cui questo codice è meno fragile di:
string x = SomeFunction ();
int l = x.Length;
È perché non possiamo chiamare la Length
su string option
. Abbiamo bisogno di estrarre il valore della string
usando la corrispondenza del modello e così facendo ci garantisce che il valore della string
è sicuro da usare.
Il modulo opzionale consente la programmazione orientata alle ferrovie
La gestione degli errori è importante ma può trasformare un elegante algoritmo in un pasticcio. ROP
( Railway Oriented Programming ) è utilizzato per rendere la gestione degli errori elegante e componibile.
Considera la semplice funzione 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
Lo scopo di f
è quello di analizzare il valore della string
input (se c'è Some
) in un int
. Se int
è maggiore di 0
lo gettiamo in un float
. In tutti gli altri casi eseguiamo il salvataggio con None
.
Sebbene, una funzione estremamente semplice, la match
annidata diminuisce significativamente la leggibilità.
ROP
osserva che abbiamo due tipi di percorsi di esecuzione nel nostro programma
- Percorso felice: alla fine calcolerà
Some
valore - Percorso errore: tutti gli altri percorsi generano
None
Poiché i percorsi di errore sono più frequenti tendono a prendere il controllo del codice. Vorremmo che il codice percorso felice fosse il percorso di codice più visibile.
Una funzione equivalente g
usa ROP
potrebbe assomigliare a questa:
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
Assomiglia molto a come tendiamo a elaborare elenchi e sequenze in F#
.
Si può vedere Option<'T>
come una List<'T>
che può contenere solo 0
o 1
elemento dove Option.bind
si comporta come List.pick
(concettualmente Option.bind
meglio a List.collect
ma List.pick
potrebbe essere più facile da capire).
bind
, filter
e map
gestisce i percorsi di errore e g
contengono solo il codice percorso felice.
Tutte le funzioni che accettano direttamente l' Option<_>
e restituiscono l' Option<_>
sono direttamente componibili con |>
e >>
.
ROP
quindi aumenta la leggibilità e la componibilità.
Utilizzo dei tipi di opzioni da C #
Non è una buona idea esporre i tipi di opzioni al codice C #, poiché C # non ha un modo per gestirli. Le opzioni sono o per introdurre FSharp.Core
come dipendenza nel tuo progetto C # (che è quello che dovresti fare se stai consumando una libreria F # non progettata per l'interoperabilità con C #), o per modificare i valori None
su null
.
Pre-F # 4.0
Il modo per farlo è creare una funzione di conversione personalizzata:
let OptionToObject opt =
match opt with
| Some x -> x
| None -> null
Per i tipi di valore dovresti ricorrere al pugilato o all'utilizzo di System.Nullable
.
let OptionToNullable x =
match x with
| Some i -> System.Nullable i
| None -> System.Nullable ()
F # 4.0
In F # 4.0, le funzioni ofObj
, toObj
, ofNullable
e toNullable
introdotte nel modulo Option
. In F # interattiva possono essere usati come segue:
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
Si noti che None
compila in null
internamente. Tuttavia, per quanto riguarda F # è 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