Это не столько ограничение GADT, сколько одно из их желаемых свойств.Когда вы пишете
type _ word =
| Arg : string -> string word
| Flag : flag -> flag word
, вы просите, чтобы типограф проверял, чтобы типы Arg _
и Flag _
были разными и несовместимыми.
Чтобы мотивировать это поведение, лучшим примером может бытьсписок со статической длиной:
type zero = Zero
type 'a succ = Succ
type ('elt,'size) nlist =
| []: ('elt, zero) nlist
| (::): 'elt * ('elt, 'size) nlist -> ('elt, 'size succ) nlist
С этим определением значение типа ('n, 's) nlist
содержит кодирование его длины внутри этого типа.Это позволяет написать общую функцию hd
let hd (a::q) = a
Поскольку наш экзотический тип списка имеет длину в своем типе, средство проверки типов может выразить тот факт, что hd
принимает список только с одним или несколькимиаргумент (т.е. тип hd ('elt,_ succ) nlist -> 'elt
).Таким образом, функция hd
всегда возвращает (когда проверяется ее тип).
Но это также означает, что средство проверки типов теперь должно принудительно применять, чем всегда работает функция hd
.Другими словами, нельзя проверять тип длины смешивания массива различного размера
[| []; [1]; [1;2] |]
, поскольку он содержит элемент, для которого функция hd
не является четко определенной, и средство проверки типов нам гарантированоранее, чем hd
всегда будет возвращать успешное значение.
Возвращаясь к своему примеру с определением типа, вы сделали возможным различение между Flag _
и Int _
.Таким образом, я могу написать следующую общую функцию
let empty (Arg _) = ""
let map_empty = Array.map empty
и ожидать, что map_empty
работает на всех типизированных массивах.Но я не могу применить эту функцию к вашему смешанному массиву
let args = [|
Arg "hello";
Flag {name = "foo"; payload = Some "world"};
|]
Другими словами, этот массив не может быть хорошо типизирован.