Szukaj…


Definicja opcji

Option jest dyskryminowanym związkiem z dwoma przypadkami: None lub Some .

type Option<'T> = Some of 'T | None

Użyj opcji <'T> ponad wartościami zerowymi

W funkcjonalnych językach programowania, takich jak F# wartości null są uważane za potencjalnie szkodliwe i kiepskie (nie idiomatyczne).

Rozważ ten kod C# :

string x = SomeFunction ();
int    l = x.Length;

x.Length wyrzuci, jeśli x jest null , dodajmy ochronę:

string x = SomeFunction ();
int    l = x != null ? x.Length : 0;

Lub:

string x = SomeFunction () ?? "";
int    l = x.Length;

Lub:

string x = SomeFunction ();
int    l = x?.Length;

W idiomatic wartości F# null nie są używane, więc nasz kod wygląda następująco:

let x = SomeFunction ()
let l = x.Length

Czasami jednak konieczne jest przedstawienie pustych lub niepoprawnych wartości. Następnie możemy użyć Option<'T> :

let SomeFunction () : string option = ...

SomeFunction albo powraca Some string wartości lub None . Wyciągamy wartość string za pomocą dopasowania wzorca

let v =
  match SomeFunction () with
  | Some x  -> x.Length
  | None    -> 0

Powód, dla którego ten kod jest mniej delikatny niż:

string x = SomeFunction ();
int    l = x.Length;

Jest tak, ponieważ nie możemy wywołać opcji Length w string option . Musimy wyodrębnić wartość string za pomocą dopasowania wzorca i dzięki temu mamy pewność, że wartość string jest bezpieczna w użyciu.

Moduł opcjonalny umożliwia programowanie zorientowane na kolej

Obsługa błędów jest ważna, ale może zmienić elegancki algorytm w bałagan. Programowanie zorientowane na kolej ( ROP ) służy do tego, aby obsługa błędów była elegancka i możliwa do skomponowania.

Rozważ prostą funkcję 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

Celem f jest przetwarzanie wejściowego string wartości (jeśli jest Some ) w jego int . Jeśli int jest większe niż 0 , rzucamy go na float . We wszystkich innych przypadkach ratujemy się z None .

Chociaż niezwykle prosta funkcja zagnieżdżonego match znacznie zmniejsza czytelność.

ROP zauważa, że mamy dwa rodzaje ścieżek wykonania w naszym programie

  1. Szczęśliwa ścieżka - w końcu obliczy Some wartość
  2. Ścieżka błędów - wszystkie inne ścieżki powodują None

Ponieważ ścieżki błędów są częstsze, mają tendencję do przejmowania kodu. Chcielibyśmy, aby szczęśliwy kod ścieżki był najbardziej widoczną ścieżką kodu.

Równoważna funkcja g korzystająca z ROP może wyglądać następująco:

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

Wygląda bardzo podobnie do tego, w jaki sposób przetwarzamy listy i sekwencje w F# .

Można zobaczyć Option<'T> jak List<'T> która może zawierać tylko 0 lub 1 element, w którym Option.bind zachowuje się jak List.pick (koncepcyjnie Option.bind odwzorowuje lepiej na List.collect ale List.pick może być łatwiejsze do zrozumienia).

bind , filter i map obsługuje ścieżki błędów g zawiera tylko kod szczęśliwej ścieżki.

Wszystkie funkcje, które bezpośrednio akceptują Option<_> i zwracają Option<_> można bezpośrednio łączyć z |> i >> .

ROP zwiększa zatem czytelność i składalność.

Korzystanie z typów opcji od C #

Nie jest dobrym pomysłem eksponowanie typów Opcji na kod C #, ponieważ C # nie ma sposobu na ich obsługę. Opcje są albo przedstawić FSharp.Core jako zależność w projekcie C # (czyli to, co trzeba było zrobić, jeśli zużywa się F # biblioteki nie przeznaczonego dla współdziałanie z C #), lub zmienić None wartości do null .

Pre-F # 4.0

Sposobem na to jest utworzenie własnej funkcji konwersji:

let OptionToObject opt =
    match opt with
    | Some x -> x 
    | None -> null

W przypadku typów wartości należy użyć ich boksu lub użycia System.Nullable .

let OptionToNullable x = 
    match x with 
    | Some i -> System.Nullable i
    | None -> System.Nullable ()

F # 4.0

W F # 4.0 ofObj , toObj , ofNullable i toNullable wprowadzone do karty Option . W interaktywnym F # można ich używać w następujący sposób:

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

Pamiętaj, że None wewnętrznie kompiluje się do null . Jednak jeśli chodzi o F #, jest to 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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow