Значение переменных типа области, стоящих для переменных типа, а не типов

В документации GHC для расширения ScopedTypeVariables в обзоре говорится следующее:

Переменная типа области обозначает переменную типа, а не тип. (Это изменение от более раннего дизайна GHC).

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

В комментарии выше говорится о двух проектах, которые по-разному подходили к этому решению и делали разные компромиссы. Каков был альтернативный дизайн и как он сравнивается с тем, который в настоящее время реализован?

Ответы

Ответ 1

tl; dr: В документации говорится, что она говорит, потому что старая реализация скопированных типизированных переменных в GHC была другой, а новая документация (более) подчеркивает контраст между старым поведением и новым поведением. Фактически, скопированные типизированные переменные, которые вы используете с расширением ScopedTypeVariables на месте, являются просто старыми (жесткими) переменными типа, и это те же переменные типа, которые вы использовали в обычных подписях типа Haskell без определения области охвата (даже если вы не использовали " я понял, что они "жесткие"). Верно, что переменные типа с областью не просто "стоят для типов", но обычные переменные типа без пробелов не просто "стоят для типов".

Более длинный ответ:

Во-первых, отменив проблему переменных типа области, рассмотрите следующее:

pluralize :: [a] -> [a]
pluralize x = x ++ "s"

Если a, как переменная типа, просто "стояла за тип", это было бы хорошо. GHC определит, что a обозначает тип Char, а результирующая сигнатура [Char] → [Char] будет определена как правильный тип pluralize, поэтому проблем не возникнет. Фактически, если бы мы вывели тип:

pluralize x = x ++ "s"

в простой старой системе типа Hindley-Milner (HM), это, вероятно, именно то, что произойдет. В качестве промежуточного шага при вводе приложения (++) средство проверки типов присваивало бы x типу [a] для "новой" переменной типа HM a и pluralize бы pluralize типа [a] → [a] до объединение [a] с типом "s" :: [Char] для объединения a и Char.

Вместо этого это отклоняется с помощью проверки типа GHC, потому a в этом типе подпись не является переменной типа типа HM и поэтому не просто обозначает тип. Вместо этого это жесткая (т.е. Заданная пользователем) переменная типа Haskell, и средство проверки типов не позволяет такой переменной объединяться с чем-то другим, кроме самого себя, при определении pluralize.

Аналогично, отклоняется следующее:

pairlist :: a -> b -> [a]
pairlist x y = [x,y]

хотя, если a и b просто стояли для типов, это было бы хорошо (поскольку оно работает для любых a и b вида * при условии, что a и b являются a и тем же типом). Вместо этого он отвергается с помощью проверки типа, потому что две жесткие переменные типа Haskell a и b не могут объединяться.

Теперь вы можете попытаться доказать, что проблема не в том, что переменные типа являются "жесткими" и не могут объединяться с конкретными типами (например, Char) или друг с другом, а скорее, что в сигнатурах типа Haskell имеется неявное количественное выражение, так что подпись для pluralize на самом деле:

pluralize :: forall a . [a] -> [a]

и поэтому, когда a определяется "стоять за" Char, это противоречие с этим forall a всей количественной оценки, которая вызывает ошибку. Проблема с этим аргументом состоит в том, что оба объяснения на самом деле более или менее эквивалентны. Это связано с тем, что переменные типа Haskell являются жесткими (т.е. Поскольку сигнатуры типов в Haskell неявно определяются количественно), что типы не могут объединяться (т.е. Унификация противоречит количественной оценке). Однако выясняется, что объяснение "жестких типов" ближе к тому, что на самом деле происходит в контролерах типа GHC, чем объяснение "неявного количественного определения". Поэтому сообщения об ошибках, генерируемые вышеуказанными определениями, относятся к неспособности сопоставлять переменные жесткого типа, а не с противоречиями с количественными количественными переменными универсального типа.

Теперь вернемся к вопросу о переменных типа области. В -fscoped-type-variables времена -fscoped-type-variables GHC -fscoped-type-variables было реализовано совершенно по-другому. В частности, для подписей типа шаблона вам было разрешено писать такие вещи, как следующее (взято из документации для GHC 6.0):

f :: [Int] -> Int -> Int
f (xs::[a]) (y::a) = (head xs + y) :: a

и в документации продолжалось:

Подписи типа шаблона в левой части f выражают тот факт, что xs должен быть списком вещей некоторого типа a; и что y должен иметь тот же тип. Подписи типа в выражении (head xs) [sic] указывает, что это выражение должно иметь один и тот же тип a. Нет требования, чтобы тип, названный " a ", фактически является переменной типа. Действительно, в этом случае тип, названный " a ", является Int. (Это небольшая либерализация из оригинальных довольно сложных правил, в которых указано, что переменная типа с привязкой к шаблону должна быть количественно определена.)

Далее были приведены некоторые дополнительные примеры использования переменных типа области, таких как:

g (x::a) (y::b) = [x,y]       -- a unifies with b

k (x::a) True    = ...        -- a unifies with Int
k (x::Int) False = ...

В 2006 году Саймон Пейтон-Джонс сделал большую фиксацию ( ac10f840), чтобы добавить непредсказуемость в систему типов, которая также существенно изменила реализацию переменных типа с лексическим охватом. Текст фиксации содержит подробное объяснение изменения, включая требования к новому дизайну.

Ключевым вариантом выбора было то, что лексические переменные типа теперь называются жесткими (то есть, заданными пользователем полиморфными) переменными типа Haskell, а не скорее похожими на переменные типа HM, которые просто стояли за тип и подлежали унификации.

Это сделало приведенные выше примеры (f, g и k) незаконными, так как переменные типа с переменными в шаблонах теперь ведут себя скорее как обычные жесткие переменные типа.

Soooo... старый дизайн, вероятно, был странным хаком, который делал переменные типа типа более похожими на переменные типа HM и настолько сильно отличался от "обычных" переменных типа Haskell, и новая система приводила их в соответствие с тем, как ведут себя переменные типа непечатаемого типа.

Однако, усложняя ситуацию, ссылка @duplode в комментариях ссылается на предложение частично "отменить" это "ограничение" в контексте подписей в шаблонах. Я считаю, что было бы справедливо сказать, что старый дизайн, который обрабатывал переменные типа с широким диапазоном, как частный случай, уступал новому дизайну, который лучше унифицировал обработку переменных типа scoped и unscoped, и нет желания возвращаться к старой реализации. Однако новая, более простая реализация имела побочный эффект от излишнего ограничения для сигнатур шаблонов, которые, возможно, следует рассматривать как частный случай, когда допустимы нежесткие переменные типа.

Ответ 2

Я добавляю этот ответ (на свой вопрос), чтобы расширять ссылку на дублирование в комментариях. ScopedTypeVariables в настоящее время изменен, чтобы позволить переменным типам типа стоять для типов вместо переменных типа. Обсуждение этого изменяет мотивацию для новых и старых проектов. Это, однако, не затрагивает даже более ранний дизайн, упомянутый в вопросе, и в ответе К. А. Бура.

В текущем состоянии, перед предстоящим изменением, определение

prefix :: a -> [[a]] -> [[a]]
prefix (x :: b) yss = map xcons yss
    where xcons ys = x : ys

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

prefix :: Int -> [[Int]] -> [[Int]]
prefix (x :: b) yss = map xcons yss
    where xcons ys = x : ys

то программа отклоняется: b запрещается стоять для Int поскольку Int не является переменной типа. Саймон Пейтон Джонс заметил, почему он был спроектирован так, чтобы b не мог стоять за Int:

В то время я был обеспокоен тем, что было бы странно иметь переменную типа, которая была просто псевдонимом для Int; это не переменная типа. Но в эти дни GADT и типа равенства мы все привыкли к этому. Сегодня у нас будет другой выбор.

В нынешнем консенсусе сторонников GHC ограничение на b стоящее за Int, рассматривается как неестественное, особенно в свете возможности равенства типов (a ~ Int) =>... Наличие таких ограничений размывает линии того, что "привязано к переменной типа" действительно означает. Если примеры

f1 :: (a ~ Int) => Maybe a -> Int
f1 (Just (x :: b)) = ...

f2 :: (a ~ Int) => Maybe Int -> Int
f2 (Just (x :: a)) = ...

разрешено? Согласно новому предложению, все четыре приведенных выше примера разрешены.


По моему мнению, напряженность в конечном счете происходит из-за сосуществования двух совершенно разных типов аннотационных систем. Один из них приводит к тому, что вы не даете разные имена одному типу (например, вы не можете писать (\x → x) :: a → b или (\x → x) :: Int → b и ожидают, что b будет унифицирован с помощью a или Int). Другой позволяет и побуждает вас давать новые имена вещам (сигнатуры типа типа, такие как foo (x :: b) =...), функция, которая существует, чтобы вы могли называть типы, которые иначе были бы непризнанными. Остается вопрос о том, должны ли шаблоны типов шаблонов разрешать вам типы псевдонимов, которые уже называются. Ответ в его основе зависит от того, какой из двух прецедентов вы найдете более убедительными.


Рекомендации:

  1. Иоахим Брейтнер "номата" (апрель-август 2018 года). Билет запроса функции "ScopedTypeVariables может позволить больше программ", https://ghc.haskell.org/trac/ghc/ticket/15050
  2. nomeata (апрель-август 2018 года). Вытянуть запрос "Разрешить ScopedTypeVariables ссылаться на типы", https://github.com/ghc-proposals/ghc-proposals/pull/128
  3. Ричард Эйзенберг, Йоахим Брейтнер и Саймон Пейтон Джонс (июнь 2018 года). "Введите переменные в шаблонах", https://www.microsoft.com/en-us/research/uploads/prod/2018/06/tyvars-in-pats-haskell18-preprint.pdf, особенно разделы 3.5 и 4.3
  4. номета (август 2018 года). Предложение GHC "Разрешить ScopedTypeVariables ссылаться на типы", https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0029-scoped-type-variables-types.rst