Давайте представим, что мы (или, скорее, и ) ocaml typechecker, и мы хотим определить тип вашего значения extract_x
let extract_x x = ...
Хорошо, так что мы знаем, чтотип extract_x
должен быть функцией, _ -> _
.Давайте назовем неизвестный на данный момент тип ввода a
и тип вывода z
, тогда переменная x
имеет тип a
:
extract_x : a -> z
x : a
... : z
Давайте посмотрим глубже:
let extract_x x = match x with
| p1 -> v1
| p2 -> v2
Здесь я назвал шаблоны и значения, чтобы не записывать их.Теперь мы знаем, что p1
и p2
должны соответствовать значениям типа a
и что v1
и v2
имеют тип z
.Давайте рассмотрим паттерны:
| Some state -> ...
| None -> ...
Итак, теперь мы можем сделать вывод, что a
должно быть чем-то вроде option
.Допустим, это b option
.Теперь у нас есть:
extract_x : b option -> z
x : b option
... : z
(In first case only):
state : b
Давайте рассмотрим первый случай:
| Some state -> state
Итак, мы теперь выводим, что z = b
, то есть тип, который должен быть возвращен из этой функции:тип state
, который мы назвали b
.Давайте рассмотрим другой случай:
| None -> None
Ну, значение (то есть бит после ->
) None
является типом параметра.Давайте назовем это c option
, и мы также знаем, что это должен быть тип z
, поэтому давайте запишем, что у нас есть:
extract_x : a -> z
a = b option
z = b
z = c option
Итак, мы выводим, что b = c option
так a = c option option
такпометив переменные произвольного типа как полиморфные, мы получим:
extract_x : 'a option option -> 'a option
Это полезная функция (в общем случае называемая bind
), но не та, которую вы хотите.Вот как вы можете написать функцию с типом 'a option -> 'a
:
let extract_x x =
match x with
| Some x -> x
| None -> failwith "extract_x: expected Some"
Однако вы, вероятно, вообще не хотите эту функцию, потому что если у вас есть опция, то это может быть None
, чтовызовет активацию этой функции, и если у вас есть опция, которая всегда Some
, тогда вы должны попытаться изменить ваши типы, чтобы выразить, что эта вещь не является дополнительной.В противном случае может показаться, что недопустимое значение None
является допустимым, поскольку оно может быть построено.
Вот как на самом деле вы можете иметь дело с извлечением значений из опций более безопасным способом:
...
match t.x with
| None -> failwith "invariant for foo violated in bar”
| Some x -> (* carry on *)
...
(* don’t do this *)
type 'a one_or_two = 'a option * 'a option
...
let (first, second) = ... : _ one_or_two in
match first with
| None ->
(* now we know second must be Some *)
let second = extract_x second in
...
(* do this instead *)
type 'a one_or_two = First of 'a | Second of 'a | Both of 'a * 'a