Поиск…
Определение варианта
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