В OCaml, какое определение типа это: a. единица → 'a
Вопросы
Впервые я увидел определение типа 'a. unit -> 'a
в Явный полиморфный тип в записи
Q1: Что это за 'a.
(обратите внимание на точку)?
Q2: Какова терминология для такого типа определения типа?
Если я делаю
let f:'a. 'a list -> int = fun l -> List.length l;;
utop показывает
val f : 'a list -> int = <fun>
Q3: Почему utot не показывает тип 'a. 'a list -> int
?
Q4. Когда следует использовать такое определение типа?
Кроме того, я могу использовать этот вид определения в записи:
type t = { f: 'a. 'a list -> int};; (* this is correct *)
но я не могу использовать его в вариантах:
type t = Node of ('a. 'a list -> int);; (* this is wrong *)
Q5: почему?
Обновление/сводка
Я провел несколько экспериментов над этим forall type definition
, поскольку не могу найти статей в Интернете об этой теме в OCaml, и я хочу, чтобы узнать, что позади.
Я суммирую эти эксперименты здесь и надеюсь, что кто-то может дать больше понимания.
Из answer ниже и его комментариев, я чувствую, что 'a.
- это своего рода force forall
вещь.
1. 'a.
в определении функции
let f:('a -> int) = fun x -> x + 1 (* correct *)
Выше это хорошо, потому что OCaml может сузить тип параметра f и заменить 'a
на int
.
Однако
let f:'a. ('a -> int) = fun x -> x + 1 (* wrong *)
Это не будет передавать компилятор, потому что для f
применим f
к all types
через 'a.. По-видимому, из части определения невозможно, поскольку единственным возможным типом для x
является int
.
Этот пример интересен, так как он показывает логику и магию для системы вывода OCaml static. Обычно типы обычно проявляются из определения функции, т.е. Вы больше заботитесь о том, что делает функция, вместо того, чтобы сначала давать тип.
Для меня очень редко используется 'a.
при определении функций, как будто определение функции может обрабатывать все типы, его тип, естественно, будет 'a.
; если функция в любом случае не может обрабатывать все типы, нет смысла форсировать все типы. Я предполагаю, что это одна из причин, почему OCaml верхнего уровня обычно не докучает его.
2, 'a.
в типе вывода
let foo f = f [1;2;3] + f [4;5;6] (* correct *)
Функция f
будет выведена как int list -> int
, потому что OCaml сначала видит [1;2;3]
и это int list
, поэтому OCaml предполагает, что f
примет int list
.
И вот почему приведенный ниже код терпит неудачу, так как второй список string list
let foo f = f [1;2;3] + f ["1";"2";"3"] (* wrong*)
Даже если я знаю, что List.length
будет хорошим кандидатом для f
, OCaml не разрешит из-за системы вывода типа.
Я думал, что если я заставляю f быть 'a.
, то f
может обрабатывать как int list
, так и string list
в foo
, поэтому я сделал:
let foo (f:'a. 'a list -> int) = f [1;2;3] + f ["1";"2";"3"];; (* wrong *)
Не удалось, и OCaml, похоже, не допустил этого. и я предполагаю, что именно поэтому вы не всегда можете делать вывод типа при наличии импрессивного полиморфизма, поэтому OCaml ограничивает его использование для записи полей и методов объектов.
3. 'a.
в записи
Обычно я беру 'a
из параметра типа, подобного этому:
type 'a a_record = {f: 'a list -> int};; (* correct *)
Тем не менее, ограничение заключается в том, что после применения вы получаете конкретный тип:
let foo t = t.f [1;2;3] + t.f [4;5;6];; (* correct *)
OCaml выведет t
как int a_record
, а не a 'a a_record
. Таким образом, нижеследующее не будет выполнено:
let foo t = t.f [1;2;3] + t.f ["1";"2";"3"];; (* wrong*)
В этом случае мы можем использовать 'a.
, поскольку OCaml разрешает его в типе записи.
type b_record = {f: 'a. 'a list -> int};; (* correct *)
let foo t = t.f [1;2;3] + t.f ["1";"2";"3"];; (* correct *)
b_record
сам по себе является конкретным типом записи, а его f
может применяться ко всем типам списков. то наш foo
выше пройдет OCaml.
Ответы
Ответ 1
'a.
означает "для всех типов" a ". Верхний уровень OCaml обычно не показывает его, поэтому он не появляется на выходе. Любое напечатанное выражение, содержащее переменные типа, имеет неявное forall в начале, если не указано иное.
При определении функции может быть полезно добавить это в начале, чтобы гарантировать, что тип является полиморфным. например.
# let f : ('a -> 'a) = fun x -> x + 1;;
val f : int -> int = <fun>
Здесь 'a -> 'a
просто ограничивает тип вывода функции таким же, как и его тип ввода, но OCaml может дополнительно ограничить его. С 'a.
этого не происходит:
# let f : 'a. ('a -> 'a) = fun x -> x + 1;;
Error: This definition has type int -> int which is less general than
'a. 'a -> 'a
Вам нужно указать 'a.
при определении типов записей или объектов, и вы хотите охватить переменную типа для отдельного элемента, а не для всего объекта.