Это не совсем ошибка, но это длинная история ...
История
В 7.0 раньше был конструктор приведения right
, который работал так:
g : f a ~ f b
---------------
right g : a ~ b
То есть, если g
является принуждением между f a
и f b
, то right g
является принуждением между a
и b
.Это только звук, если f
гарантированно будет инъективным: в противном случае мы могли бы на законных основаниях, скажем, f Int ~ f Char
, и тогда мы могли бы заключить Int ~ Char
, что было бы Плохо.Конечно, синонимы типов и семейства типов не обязательно являются инъективными;например:
type T a = Int
type family F a :: *
type instance F Int = Bool
type instance F Char = Bool
Так как же эта гарантия возможна?Что ж, это как раз та причина, по которой частичное применение синонимов и семейств типов не допускается.Частичные применения синонимов типов и семейств типов могут не быть инъективными, но насыщенные приложения (даже те, которые приводят к более высокому виду) всегда.
Конечно, ограничение на частичные применения раздражает.Таким образом, в 7.2, пытаясь двигаться в направлении разрешения частичного применения (и поскольку это упрощает теорию и реализацию языка принуждения), конструктор right
был заменен конструктором nth
с сопровождающим правилом
g : T a1 .. an ~ T b1 .. bn
---------------------------
nth i g : ai ~ bi
То есть nth
применяется только к принуждению g
, которое относится к двум типам, которые известны как насыщенные приложения конструктора типа T
.Теоретически, это допускает частичное применение синонимов и семейств типов, потому что мы не можем разложить равенства, пока не узнаем, что они находятся между приложениями (обязательно инъективного) конструктора типа.В частности, nth
не применяется к принуждению f a ~ f b
, потому что f
является переменной типа, а не конструктором типа.
Во время изменения считалось, что никто на самом деле не заметит, но, очевидно, это было неправильно!
Интересно, что трюк Олега, изложенный в сообщении haskell-cafe от Daniel Schüssler , показывает, что реализация семейств типов не изменилась соответствующим образом!Проблема в том, что определение типа
type family Arg fa
type instance Arg (f a) = a
не должно быть разрешено, если f
может быть неинъективным;в этом случае определение даже не имеет смысла.
Следующие шаги
Я думаю, что правильное решение - восстановить right
(или что-то эквивалентное), поскольку люди явно этого хотят!Надеемся, что это будет сделано в ближайшее время.
Тем временем было бы очень здорово разрешить частично примененные синонимы и семейства типов.Кажется, правильный путь (tm) сделать это - отслеживать инъективность в системе вида: то есть каждый вид стрелки будет аннотирован своей инъекцией.Таким образом, встречая равенство f a ~ f b
, мы можем посмотреть на вид f
, чтобы определить, безопасно ли его разложить на равенство a ~ b
.Не случайно я сейчас пытаюсь разработать дизайн такой системы.=) * +1058 *