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à
Somevalore - 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