Подтвердить что ты не робот

Могут ли GADT использоваться для доказательства неравенств типов в GHC?

Итак, в моих постоянных попытках наполовину понять Карри-Говарда с помощью небольших упражнений Хаскелла, я застрял в этом пункте:

{-# LANGUAGE GADTs #-}

import Data.Void

type Not a = a -> Void

-- | The type of type equality proofs, which can only be instantiated if a = b.
data Equal a b where
    Refl :: Equal a a

-- | Derive a contradiction from a putative proof of @Equal Int [email protected]
intIsNotChar :: Not (Equal Int Char)
intIsNotChar intIsChar = ???

Очевидно, что тип Equal Int Char не имеет (не-нижних) обитателей и, следовательно, семантически должен быть функцией absurdEquality :: Equal Int Char -> a... но для жизни меня я не могу найти способ написать один кроме использования undefined.

Итак, либо:

  • Мне что-то не хватает, или
  • Существует некоторое ограничение языка, делающего эту невозможную задачу, и мне не удалось понять, что это такое.

Я подозреваю, что ответ таков: компилятор не может использовать тот факт, что нет конструкторов Equal, у которых нет a = b. Но если это так, что делает его истинным?

4b9b3361

Ответ 1

Здесь более короткая версия решения Philip JF, которая является методом зависимых типов, опровергала уравнения в течение многих лет.

type family Discriminate x
type instance Discriminate Int  = ()
type instance Discriminate Char = Void

transport :: Equal a b -> Discriminate a -> Discriminate b
transport Refl d = d

refute :: Equal Int Char -> Void
refute q = transport q ()

Чтобы показать, что все по-другому, вы должны поймать их по-другому, предоставляя вычислительный контекст, который приводит к отдельным наблюдениям. Discriminate предоставляет именно такой контекст: программа уровня уровня, которая обрабатывает два типа по-разному.

Нет необходимости прибегать к undefined для решения этой проблемы. Общее программирование иногда включает отказ от невозможных входов. Даже там, где доступно undefined, я бы рекомендовал не использовать его там, где достаточно полного метода: общий метод объясняет, почему что-то невозможно, и подтверждение typechecker; undefined просто документирует ваше обещание. Действительно, этот метод опровержения заключается в том, как Эпиграмм распределяет "невозможные случаи", гарантируя, что анализ случаев охватывает его домен.

Что касается вычислительного поведения, обратите внимание, что refute, через transport обязательно является строгим в q и что q не может вычислять головную форму в пустом контексте просто потому, что такая нормальная форма головы не существует ( и потому, что вычисление сохраняет тип, конечно). В общей настройке мы будем уверены, что refute никогда не будет вызываться во время выполнения. В Haskell мы, по крайней мере, уверены, что его аргумент расходится или генерирует исключение, прежде чем мы обязаны ответить на него. Ленивая версия, например

absurdEquality e = error "you have a type error likely to cause big problems"

будет игнорировать токсичность e и сказать вам, что у вас есть ошибка типа, когда вы этого не делаете. Я предпочитаю

absurdEquality e = e `seq` error "sue me if this happens"

если честное опровержение слишком похоже на тяжелую работу.

Ответ 2

Я не понимаю проблемы с использованием undefined каждый тип заселен дном в Haskell. Наш язык не сильно нормализуется... Вы ищете не то. Equal Int Char приводит к ошибкам типа не очень хорошо сохранившимся исключениям. См

{-# LANGUAGE GADTs, TypeFamilies #-}

data Equal a b where
    Refl :: Equal a a

type family Pick cond a b
type instance Pick Char a b = a
type instance Pick Int a b = b

newtype Picker cond a b = Picker (Pick cond a b)

pick :: b -> Picker Int a b
pick = Picker

unpick :: Picker Char a b -> a
unpick (Picker x) = x

samePicker :: Equal t1 t2 -> Picker t1 a b -> Picker t2 a b
samePicker Refl x = x

absurdCoerce :: Equal Int Char -> a -> b
absurdCoerce e x = unpick (samePicker e (pick x))

вы можете использовать это, чтобы создать нужную функцию.

absurdEquality e = absurdCoerce e ()

но это приведет к выполнению undefined поведения в качестве его правила вычисления. false должен прерывать программы или, по крайней мере, работать навсегда. Прерывание - это правило вычисления, которое сродни превращению минимальной логики в интуитивно-понятную логику путем добавления нет. Правильное определение

absurdEquality e = error "you have a type error likely to cause big problems"

относительно вопроса в названии: по существу нет. Насколько я знаю, типовое неравенство не реализуется практично в текущем Haskell. Приход изменений в систему типов может привести к тому, что это станет лучше, но на данный момент у нас есть равенства, но не неравенства.