Zoeken…
Definitie van optie
Een Option
is een gediscrimineerde unie met twee gevallen, None
of Some
.
type Option<'T> = Some of 'T | None
Gebruik optie <'T> boven nulwaarden
In functionele programmeertalen zoals F#
null
als potentieel schadelijk en slechte stijl (niet-idiomatisch) beschouwd.
Beschouw deze C#
code:
string x = SomeFunction ();
int l = x.Length;
x.Length
werpt als x
null
laten we bescherming toevoegen:
string x = SomeFunction ();
int l = x != null ? x.Length : 0;
Of:
string x = SomeFunction () ?? "";
int l = x.Length;
Of:
string x = SomeFunction ();
int l = x?.Length;
In idiomatische F#
null
waarden niet gebruikt, dus onze code ziet er zo uit:
let x = SomeFunction ()
let l = x.Length
Soms is het echter nodig om lege of ongeldige waarden weer te geven. Dan kunnen we Option<'T>
:
let SomeFunction () : string option = ...
SomeFunction
ofwel rendement Some
string
waarde of None
. We extraheren de string
met behulp van patroonovereenkomst
let v =
match SomeFunction () with
| Some x -> x.Length
| None -> 0
De reden dat deze code minder kwetsbaar is dan:
string x = SomeFunction ();
int l = x.Length;
Dat komt omdat we Length
niet kunnen aanroepen op een string option
. We moeten de string
extraheren met behulp van patroonvergelijking en hierdoor zijn we ervan verzekerd dat de string
veilig is om te gebruiken.
Optiemodule maakt Railway Oriented Programming mogelijk
Foutafhandeling is belangrijk, maar kan van een elegant algoritme een puinhoop maken. Railway Oriented Programming ( ROP
) wordt gebruikt om foutafhandeling elegant en configureerbaar te maken.
Overweeg de eenvoudige functie 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
Het doel van f
is de ingang ontleden string
waarde (als er Some
) in een int
. Als de int
groter is dan 0
, werpen we het in een float
. In alle andere gevallen redden we met None
.
Hoewel, een uiterst eenvoudige functie de geneste match
de leesbaarheid aanzienlijk vermindert.
ROP
merkt op dat we twee soorten uitvoeringspaden in ons programma hebben
- Gelukkig pad - zal uiteindelijk
Some
waarde berekenen - Foutpad - Alle andere paden produceren
None
Omdat de foutpaden vaker voorkomen, nemen ze de code over. We willen graag dat de happy path-code het meest zichtbare codepad is.
Een equivalente functie g
met ROP
kan er zo uitzien:
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
Het lijkt veel op hoe we de neiging hebben om lijsten en sequenties in F#
te verwerken.
Men ziet een Option<'T>
zoals een List<'T>
die alleen 0
of 1
element kan bevatten waar Option.bind
zich gedraagt als List.pick
(conceptueel Option.bind
kaarten beter naar List.collect
maar List.pick
kan zijn gemakkelijker te begrijpen).
bind
, filter
en map
behandelt de foutpaden en g
bevat alleen de happy path-code.
Alle functies die direct accepteert Option<_>
en keert terug Option<_>
direct composable met |>
en >>
.
ROP
verhoogt daarom de leesbaarheid en de configureerbaarheid.
Optietypes van C # gebruiken
Het is geen goed idee om Optietypen bloot te stellen aan C # -code, omdat C # geen manier heeft om ze aan te pakken. De opties zijn om FSharp.Core
te introduceren als een afhankelijkheid in uw C # -project (wat u zou moeten doen als u een F # -bibliotheek gebruikt die niet is ontworpen voor interop met C #), of om None
waarden te wijzigen in null
.
Pre-F # 4.0
De manier om dit te doen, is een eigen conversiefunctie maken:
let OptionToObject opt =
match opt with
| Some x -> x
| None -> null
Voor waardetypen moet u hun toevlucht nemen tot boksen of het gebruik van System.Nullable
.
let OptionToNullable x =
match x with
| Some i -> System.Nullable i
| None -> System.Nullable ()
F # 4.0
In F # 4.0, de functies ofObj
, toObj
, ofNullable
en toNullable
waarbij kennis met de Option
-module. In F # interactive kunnen ze als volgt worden gebruikt:
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
Merk op dat None
compileert om null
intern. Wat F # betreft, is het echter 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