Поиск…
Определение варианта
Option - это дискриминационный союз с двумя случаями: « None или « Some .
type Option<'T> = Some of 'T | None
Используйте опцию <'T> над нулевыми значениями
В языках функционального программирования, таких как значения F# null , считается потенциально опасным и плохим стилем (неидиоматическим).
Рассмотрим этот код C# :
string x = SomeFunction ();
int l = x.Length;
x.Length будет бросать, если x равно null , добавим защиту:
string x = SomeFunction ();
int l = x != null ? x.Length : 0;
Или же:
string x = SomeFunction () ?? "";
int l = x.Length;
Или же:
string x = SomeFunction ();
int l = x?.Length;
В идиоматических значениях F# null не используются, поэтому наш код выглядит так:
let x = SomeFunction ()
let l = x.Length
Однако иногда возникает необходимость в представлении пустых или недопустимых значений. Затем мы можем использовать Option<'T> :
let SomeFunction () : string option = ...
SomeFunction либо возвращает Some string значение, либо None . Мы извлекаем string значение с использованием соответствия шаблону
let v =
match SomeFunction () with
| Some x -> x.Length
| None -> 0
Причина этого кода менее хрупкая:
string x = SomeFunction ();
int l = x.Length;
Это потому, что мы не можем вызывать Length по string option . Нам нужно извлечь string значение с помощью сопоставления шаблонов, и тем самым мы гарантируем, что string значение будет безопасно использовать.
Опциональный модуль позволяет навигация по железной дороге
Обработка ошибок важна, но может превратить изящный алгоритм в беспорядок. Жестко ориентированное программирование ( ROP ) используется для упрощения обработки ошибок и создания композиций.
Рассмотрим простую функцию 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
Целью f является для разбора входной string значения (если есть Some ) в int . Если int больше 0 мы превращаем его в float . Во всех остальных случаях мы спасаемся с None .
Несмотря на то, что чрезвычайно простая функция вложенного match значительно уменьшает читаемость.
ROP отмечает, что у нас есть два пути выполнения в нашей программе
- Счастливый путь - в конечном итоге вычислит
Someзначение - Путь ошибок - все остальные пути не
None
Поскольку пути ошибок более часты, они, как правило, используют код. Нам бы хотелось, чтобы код счастливого пути был наиболее видимым кодом.
Эквивалентная функция g использующая ROP может выглядеть так:
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
Это очень похоже на то, как мы склонны обрабатывать списки и последовательности в F# .
Вы можете увидеть Option<'T> как List<'T> который может содержать только 0 или 1 элемент, где Option.bind ведет себя как List.pick (концептуально Option.bind лучше отображает List.collect но List.pick может быть легче понять).
bind , filter и map обрабатывает пути ошибок, а g содержит только код счастливого пути.
Все функции, которые непосредственно принимают Option<_> и возвращают Option<_> , напрямую могут быть скомпилированы с помощью |> и >> .
Таким образом, ROP повышает читаемость и способность к сшиванию.
Использование типов опций из C #
Не рекомендуется выводить типы опций на код C #, так как у C # нет способа справиться с ними. Варианты - либо ввести FSharp.Core как зависимость в вашем проекте C # (что вам нужно сделать, если вы потребляете библиотеку F #, не предназначенную для взаимодействия с C #), либо изменить значения None на null .
Pre-F # 4.0
Способ сделать это - создать собственную функцию преобразования:
let OptionToObject opt =
match opt with
| Some x -> x
| None -> null
Для типов значений вам придется прибегать к боксу или использовать System.Nullable .
let OptionToNullable x =
match x with
| Some i -> System.Nullable i
| None -> System.Nullable ()
F # 4.0
В F # 4.0 функции ofObj , toObj , ofNullable и toNullable если они введены в модуль Option . В F # interactive они могут использоваться следующим образом:
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
Обратите внимание, что None компилируется с null внутренним. Однако, что касается F #, это 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