option вместо null
Решаем «проблему на миллиард долларов» — отсутствие значения — типобезопасно, с помощью типа option.
option — стандартный вариантный тип
None | Some of 'a, который явно выражает «значение может отсутствовать».
Тони Хоар назвал изобретение null своей «ошибкой на миллиард долларов»: ссылка, которая может оказаться пустой, но об этом не сказано в типе, порождает бесконечные падения. В OCaml нет null вообще. Отсутствие значения выражается типом option — и компилятор заставляет обработать пустой случай.
Определение и использование
type 'a option = None | Some of 'a
Функция, которая может не вернуть результат, возвращает 'a option:
let safe_div a b =
if b = 0 then None
else Some (a / b)
let show = function
| None -> "деление на ноль"
| Some q -> "результат: " ^ string_of_int q
Тип safe_div — int -> int -> int option. Вызывающий код обязан разобрать оба случая через match.
Где option встречается
Многие стандартные функции возвращают option вместо исключения. List.find_opt ищет элемент и возвращает None, если не нашёл; List.assoc_opt ищет по ключу. Суффикс _opt — соглашение «вернёт option вместо исключения».
let users = [(1, "Аня"); (2, "Иван")]
let name = List.assoc_opt 2 users (* Some "Иван" *)
let miss = List.assoc_opt 9 users (* None *)
Удобная работа: модуль Option
Option.value (Some 5) ~default:0 (* 5 *)
Option.value None ~default:0 (* 0 *)
Option.map (fun x -> x * 2) (Some 3) (* Some 6 *)
Option.map применяет функцию «внутри» Some, оставляя None как есть. Option.bind сцепляет операции, каждая из которых сама может вернуть None.
Как работает под капотом
None компилятор представляет как целое-константу, а Some x — как блок с одним полем. Накладные расходы минимальны. Главная ценность в системе типов: тип int option и int — разные, и компилятор не позволит использовать «возможно отсутствующее» число там, где нужно гарантированное. В отличие от null, который имеет тип любого объекта, option делает возможное отсутствие частью контракта. Это та же идея, что Option/Maybe в Haskell, F# и Scala — все они пришли из ML.
Частые ошибки
- Распаковывать
option«насильно». Не игнорируйтеNone, обрабатывайте его. - Возвращать «магические» значения вроде
-1вместоNone. Это возвращает все проблемыnull. - Путать
Some xиx. Это значения разных типов; чтобы добраться доx, нужно разобратьSome.
Итоги
option(None | Some of 'a) делает отсутствие значения явным в типе — null в OCaml не существует.- Компилятор требует обработать случай
None, устраняя целый класс ошибок. - Модуль
Option(value,map,bind) упрощает работу без громоздкихmatch.