Вы можете выполнить то, что хотите, используя полиморфные варианты :
type vector = [
| `Vector2D(float, float)
| `Vector3D(float, float, float)
];
let first = (a: [`Vector2D(float, float)]) => {
switch(a) {
| `Vector2D(x, y) => x +. y
}
}
let second = (`Vector2D(x, y)) => {
x +. y
}
Это даст вам ошибку типа, если вы попытаетесь передать `Vector3D
любой из функций:
^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type [> `Vector3d(float, float, float) ]
but an expression was expected of type [< `Vector2D(float, float) ]
The second variant type does not allow tag(s) `Vector3d
Обратите внимание, что Vector2D
и Vector3D
в вашем примере не являются типами. Они являются конструкторами, которые создают значения типа vector('a)
. Как вы правильно заметили, их иногда также называют «тегами», а тип иногда называют «теговым объединением» вместо «варианта», потому что значения содержат тег, который указывает, какой конструктор использовался для его создания. Этот тег проверяется при сопоставлении с шаблоном с использованием шаблона конструктора.
То, что вы используете в своем примере, является не обычным вариантом, а Обобщенным алгебраическим типом данных (GADT) (который мог бы быть более удачно назван «Обобщенный вариант» в OCaml / Reason), , и причина, по которой он не работает, заключается в том, что, хотя GADT позволяет назначать конкретный тип конструктору, он не работает с другимнаоборот. Например, у вас может быть несколько конструкторов, которые указывают vector(two)
. (Редактировать: Это кажется неправильным, см. Ответ @ octachron)
Ни одна из функций first
или third
в вашем примере не компилируется, Компилятор ожидает кортеж, потому что вы используете паттерн кортежа вместо паттерна конструктора. (x, y)
не совпадает с Vector2D(x, y)
. Если бы это было так, вы не смогли бы отличить Vector2D(float, float)
от Line(point, point)
, например, который содержит данные совершенно другого типа.