Можем ли мы иметь переменные типа в позиции конструктора в системе типов Хиндли Милнера?

В Haskell мы можем написать следующий тип данных:

data Fix f = Fix { unFix :: f (Fix f) }

Переменная типа f имеет вид * -> * (т.е. это конструктор неизвестного типа). Следовательно, Fix имеет вид (* -> *) -> *. Мне было интересно, был ли Fix допустимым конструктором типа в системе типов Хиндли Милнера.

Из того, что я прочитал в Википедии, кажется, что Fix не является действительным конструктор типа в системе типов Хиндли Милнера, потому что все переменные типа в HM должны быть конкретными (т. е. должны иметь вид *). Так ли это на самом деле? Если бы переменные типа в HM не всегда были конкретными, то стало бы HM неразрешимым?


person Aadit M Shah    schedule 06.05.2016    source источник


Ответы (2)


Важно то, образуют ли конструкторы типов язык терминов первого порядка (без редукционного поведения выражений конструкторов типов) или язык более высокого порядка (с лямбдами или аналогичными конструкциями на уровне типа).

В первом случае ограничения, возникающие из Fix, всегда универсальны в самом общем виде (при условии, что мы придерживаемся HM). В каждом c a b ~ t уравнении t должен быть преобразован в выражение приложения конкретного типа с той же формой, что и c a b, поскольку c a b не может быть преобразован в какое-либо другое выражение. Параметры более высокого порядка не являются проблемой, поскольку они тоже просто статичны, например, c [] ~ c f решается f = [].

В последнем случае c a b ~ t может или не может быть разрешимым. В некоторых случаях это решает c = \a b -> t, в других случаях нет универсального унификатора.

person András Kovács    schedule 06.05.2016

Высшие виды выходят за рамки базовой системы типов Хиндли-Милнера, но с ними можно обращаться таким же образом.

Грубо говоря, HM анализирует синтаксическое дерево выражения, связывает переменную свободного типа с каждым подвыражением и генерирует набор эквациональных ограничений для терминов типов, включающих переменные типа, в соответствии с правилами типизации. То же самое можно сделать и с высшими видами.

Затем ограничения решаются путем унификации. Типичный шаг в алгоритме объединения (далее псевдохаскелл)

(F t1 ... tn := G s1 ... sk) =
  | n/=k || F/=G  -> fail
  | otherwise     -> { t1 := s1 , ... , tn := sn }

(Обратите внимание, что это только часть алгоритма унификации.)

Выше F, G - символы конструктора типов, а не переменные. В унификации более высокого порядка нам нужно учитывать, что _4 _, _ 5_ также являются переменными. Мы могли бы попробовать следующее правило:

(f t1 ... tn := g s1 ... sk) =
  | n/=k          -> fail
  | otherwise     -> { f := g , t1 := s1 , ... , tn := sn }

Но ждать! Вышеупомянутое неверно, поскольку, например, f Int ~ Either Bool Int должен унифицироваться, когда f ~ Either Bool. Итак, нам нужно также рассмотреть случай, когда n/=k. В общем, простой набор правил

(f t := g s) =
  { f := g , t := s }
(F := G) =      -- rule for atomic terms
  | F /= G    -> fail
  | otherwise -> {}

(Опять же, это только часть алгоритма объединения. Другие случаи также должны быть обработаны, как указывает Андреас Россберг ниже.)

person chi    schedule 06.05.2016
comment
Думаю, этот ответ вводит в заблуждение. Объединение высшего порядка сложнее этого. Например, я не вижу, чтобы ваши правила обрабатывали такие базовые случаи, как (F := g t). В общем, единственного решения не существует, и проблема неразрешима. Чтобы этого избежать, вам нужно сильно ограничить проблемное пространство (что делает Haskell, например, ограничивая использование синонимов типов). - person Andreas Rossberg; 06.05.2016
comment
@AndreasRossberg Я явно избегал упоминания унификации высшего порядка в своем ответе, поскольку высшие виды этого не требуют. Кроме того, приведенное выше правило унификации не предназначено для предоставления полного алгоритма, а только для шага (типичный шаг в алгоритме унификации ...). Я постараюсь прояснить эти моменты, чтобы избежать путаницы. - person chi; 06.05.2016
comment
ну, в общем, более высокие типы действительно требуют унификации более высокого порядка. Таким образом, ответ, вероятно, должен прояснить, какие ограничения вы предполагаете на языке. - person Andreas Rossberg; 06.05.2016
comment
@AndreasRossberg Я не могу не думать, что вы ошибаетесь. Во-первых, синонимы типа совершенно не связаны с переменными типа более высокого порядка. У вас может быть язык с синонимами типов, но без переменных типа более высокого типа или (что еще хуже!) Язык с переменными типа более высокого типа и синонимами без типа. (Хуже всего Java, в которой нет синонимов типов и только * родственные типовые переменные). Кроме того, AFAIK, синонимы типов разрешаются задолго до проверки типов в GHC, поэтому они вообще не имеют значения при проверке типа / типа. - person Ingo; 06.05.2016
comment
@Ingo, рассмотрите определения типов type T a = a; type U a = Int и проблему унификации (c Int == Int). И T, и U были бы решениями для c на неограниченном языке. Но Haskell требует, чтобы конструкторы синонимов типов всегда применялись полностью (!), И, более того, не учитывает их (или, в более общем смысле, лямбды типов) во время унификации, о которых я упоминал. - person Andreas Rossberg; 06.05.2016
comment
@AndreasRossberg, неограниченного языка не существует. В Haskell T и U не являются типами, поэтому, естественно, они не являются возможными значениями для переменной типа c. Язык, в котором отсутствует \a -> a как тип, является не большим ограничением, чем отсутствие зависимых типов, или линейных типов, или более высоких индуктивных типов, или любой другой особенности системы типов. - person Reid Barton; 07.05.2016
comment
@AndreasRossberg Как я уже говорил, псевдонимы типов больше не существуют во время проверки типов. Следовательно, если такая проблема унификации возникает при проверке типов, это явное указание на ошибку типа. Это может возникнуть, например, с чем-то u :: c Int -> c Bool; u = undefined; u 42, но синонимы типов никогда не консультируются для возможного решения. (Однако насчет типовых семейств я не так уверен. Но в любом случае это выходит за рамки HM.) - person Ingo; 07.05.2016
comment
@Ingo, что язык определяет синонимы, которые больше не существуют или не являются типами, - это просто еще один способ сказать, что язык сильно ограничивает лямбды типов, то есть более высокие типы. На более высоком уровне он позволяет создавать экземпляры переменных только номинальным типам (т. Е. Константам). Сравните это с полной общностью Системы F-омега (лямбда-исчисление с высшими видами), где таких ограничений не существует. - person Andreas Rossberg; 07.05.2016
comment
@ReidBarton, я не согласен с аналогией. Haskell действительно вводит более высокие типы и лямбды типов (в форме синонимов), но делает их гражданами второго сорта. Опять же сравните с Ф-омегой. Тот факт, что вы можете частично применять конструкторы типов номинальных, но не структурные, явно является ограничением. И вся его цель - сделать вывод типа разрешимым. - person Andreas Rossberg; 07.05.2016
comment
@AndreasRossberg: да, я понимаю, но речь шла не о системе F, а конкретно о HM. Теперь ответ полностью адекватен в контексте HM, и все же вы называете его вводящим в заблуждение, потому что унификация более высокого порядка (чего не происходит в HM по уважительной причине) более сложна. Для меня это не имеет смысла. - person Ingo; 07.05.2016
comment
@Ingo, суть в том, что нельзя просто обобщить HM на более высокие типы и ожидать, что это сработает. Вы должны быть очень осторожны, как и в какой степени их добавлять. Например, ограничить их номинальными типами. Только тогда не происходит объединения более высокого порядка; нет автоматизма. - person Andreas Rossberg; 07.05.2016