Перекрывающее ограничение подтипа шаблонного шаблона в полиморфном варианте

Учитывая эти типы

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

Вопросы:

  1. Почему он больше не может принимать подтип? Я понимаю, что подстановочный знак означает, что теперь он может принимать супертип, но это не должно означать, что он ДОЛЖЕН.
  2. Есть ли способ обойти это, чтобы избежать необходимости перечислять миллион или около того вариантов, которые не актуальны?

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),

Другие вопросы по тегам