Поиск…


Определение варианта

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 отмечает, что у нас есть два пути выполнения в нашей программе

  1. Счастливый путь - в конечном итоге вычислит Some значение
  2. Путь ошибок - все остальные пути не 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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow