Перекрывающее ограничение подтипа шаблонного шаблона в полиморфном варианте
Учитывая эти типы
type a = [ `A ]
type b = [ a | `B | `C ]
и эта функция
let pp: [< b] -> string =
function | `A -> "A"
| `B -> "B"
| `C -> "C"
применяя значение типа a
работает без проблем, как и ожидалось:
let a: a = `A
let _ = pp a
Однако, если функция модифицирована для включения шаблона подстановки
let pp: [< b] -> string =
function | `A -> "A"
| `B -> "B"
| _ -> "?"
но все остальное остается прежним, теперь выдает следующую ошибку (на let _ = pp a
):
Это выражение имеет тип b -> string, но ожидалось выражение типа a -> 'a Тип b = [ `A | `B ] несовместим с типом a = [ `A ] Второй вариантный тип не допускает тег (ы) `B
Вопросы:
- Почему он больше не может принимать подтип? Я понимаю, что подстановочный знак означает, что теперь он может принимать супертип, но это не должно означать, что он ДОЛЖЕН.
- Есть ли способ обойти это, чтобы избежать необходимости перечислять миллион или около того вариантов, которые не актуальны?
2 ответа
Основной вопрос заключается в том, почему тип
let pp= function
| `A -> "A"
| `B -> "B"
| _ -> "?"
выводится как [> `A| `B] -> string
а не как [< `A| `B | ... ] -> string
(где ...
выступает за любого конструктора). Ответ таков: выбор дизайна и вопрос компромисса между ложноположительным и ложноотрицательным: https://www.math.nagoya-u.ac.jp/~garrigue/papers/matching.pdf.
Точнее, второй тип считался слишком слабым, поскольку было слишком легко потерять информацию, `A
а также `B
присутствовали в pp
, Например, рассмотрим следующий код, где `b
это орфографическая ошибка и должна была `B
:
let restrict (`A | `b) = ()
let dual x = restrict x, pp x
В настоящее время этот код не работает с
Ошибка: это выражение имеет тип [<`A | `b], но ожидалось выражение типа [>`A | `B ]
Первый вариант типа не допускает тег (ы) `B
На данный момент, если `b
была орфографическая ошибка, становится возможным поймать ошибку здесь. Если pp
был напечатан [< `A|`B |..]
тип двойной был бы ограничен [`A] -> unit * string
молча, без шансов поймать эту ошибку. Более того, при текущем наборе, если `b
не было орфографической ошибки, вполне возможно сделать dual
действителен, добавив некоторые принуждения
let dual x = restrict x, pp (x:[`A]:>[>`A]);;
(* or *)
let dual x = restrict x, (pp:>[`A] -> _) x
делая это очень ясно, что restrict
а также pp
работает на разных наборах полиморфных вариантов.
Тип второй версии pp
является [< b > `A `B ] -> string
, Другими словами, `A
а также `B
должен появиться в типе. Я думаю, это кажется разумным, если вы хотите сравнить значение с `B
затем `B
должен появиться в типе значения.
Ты можешь написать pp (a :> b)
,