option вместо null: безопасность по типам
F# заменяет коварный null типом option — отсутствие значения становится видимым в системе типов.
option — встроенное размеченное объединение с двумя вариантами:
Some значение(значение есть) иNone(значения нет).
Проблема null
В C#/Java null прячется в любой ссылке: можно забыть проверку и получить NullReferenceException в рантайме. F# в своём коде не использует null для собственных типов — отсутствие значения выражают явным option, и компилятор заставляет его обработать.
let findUser id =
if id = 1 then Some "Алиса"
else None
printfn "%A" (findUser 1)
printfn "%A" (findUser 99)Вывод:
Some "Алиса" None
Тип findUser — int -> string option. Сигнатура честно говорит: результат может отсутствовать.
Разбор option
Достать значение можно только разобрав оба случая — забыть None не получится.
let greet id =
match findUser id with
| Some name -> sprintf "Привет, %s!" name
| None -> "Пользователь не найден"
let findUser id = if id = 1 then Some "Алиса" else None
printfn "%s" (greet 1)
printfn "%s" (greet 99)Вывод:
Привет, Алиса! Пользователь не найден
Работа без распаковки: map и defaultValue
Часто значение преобразуют, не доставая его вручную. Option.map применяет функцию к содержимому Some, а к None ничего не делает. Option.defaultValue подставляет значение по умолчанию вместо None.
let result =
Some 10
|> Option.map (fun x -> x * 2)
|> Option.defaultValue 0
printfn "%d" result
let empty =
None
|> Option.map (fun x -> x * 2)
|> Option.defaultValue 0
printfn "%d" emptyВывод:
20 0
Связывание вычислений: bind
Когда следующий шаг сам возвращает option, используют Option.bind — он «протягивает» None через всю цепочку, не вызывая последующие шаги.
let parsePositive (s: string) =
match System.Int32.TryParse s with
| true, n when n > 0 -> Some n
| _ -> None
let doubled = parsePositive "21" |> Option.map (fun n -> n * 2)
printfn "%A" doubledВывод:
Some 42
Как работает под капотом
option — это обычный DU: type Option<'T> = None | Some of 'T. None представлен как пустой случай, Some хранит значение. Компилятор не позволяет обратиться к содержимому, не разобрав вариант, — отсюда безопасность. Функции map/bind/defaultValue — просто удобные обёртки над сопоставлением.
Частые ошибки
- Грубо «вытаскивать» значение через
.Valueбез проверки — наNoneэто бросит исключение. - Возвращать
nullиз F#-кода вместоNone— теряется типобезопасность. - Путать
Option.map(функция возвращает обычное значение) иOption.bind(функция возвращает option).
Итоги
optionзаменяетnull:Some x— есть значение,None— нет.- Сигнатура с
optionчестно сообщает о возможном отсутствии значения. - Компилятор требует обработать оба случая — забыть
Noneнельзя. map,defaultValue,bindпозволяют работать сoptionбез ручной распаковки.