Благодаря bergey и chi, я думаю, что понимаю последовательность шагов, которые заставили GHC вернуть эту ошибку мне.Они оба являются хорошими ответами, но я думаю, что большая часть моего недопонимания заключалась в том, что я не понимал конкретные шаги, предпринимаемые Haskell для проверки типов, и как это связано, в данном случае, с сопоставлением с шаблоном GADT.Я собираюсь написать ответ, который описывает это в меру моего понимания.
Итак, для начала, одна из вещей, которые делают GADT GADT, состоит в том, что вы можете определить тип суммы, где каждый параметр можетбыть другого типа, который является более конкретным, чем тип, указанный в заголовке объявления данных.Это делает возможным следующее:
data a :~: b where
Refl :: a :~: a
Так что здесь у нас есть только один конструктор, Refl
, который является a :~: b
, но более конкретно, этот конкретный конструктор (хотя и единственный) приводит кa :~: a
.Если мы составим это с Maybe
, чтобы получить тип Maybe (a :~: b)
, то у нас будет 2 возможных значения: Just Repl :: Maybe (a :~: a)
и Nothing :: Maybe (a :~: b)
.Вот как тип переносит информацию о равенстве типов путем сопоставления с образцом.
Теперь, чтобы заставить GADT работать с сопоставлением с образцом, нужно сделать что-то классное.Дело в том, что выражения, сопоставленные с каждым шаблоном, могут быть более специализированными, чем все выражение соответствия шаблону (например, выражения case).Использование дополнительного уточнения типа, включенного в конструктор GADT, для дальнейшей специализации типа, требуемого для соответствующего выражения, - это специальная обработка, которую Haskell выполняет для GADT при сопоставлении с образцом.Поэтому, когда мы делаем:
readMaybeWithGoodType :: forall a. (Typeable a, Read a) => String -> Maybe a
readMaybeWithGoodType = case eqT @a @String of
Just Refl -> Just
_ -> readMaybe
eqT
равно Maybe (a :~: b)
, eqT @a @String
, и совпадает _
равно (Typeable a, Read a) => Maybe (a :~: String)
, но Just Refl
равно Maybe (String :~: String)
.
Haskell потребует, чтобы целое выражение case было расширенным типом (Typeable a, Read a) => String -> Maybe a
.Совпадение _
, равное readMaybe
, имеет тип Read a => String -> Maybe a
, который является надмножеством.Тем не менее, Just
- это тип a -> Maybe a
, который не является надмножеством, потому что выражение case должно включать в себя такие вещи, как String -> Maybe Int
, но Just
не может вернуть это, потому что это потребуется для String ~ Int
.Это то, что происходило при совпадении с Bool
.GHC сказал, что он не может соответствовать, Maybe String
Just
вернется с более общим Read a => Maybe a
, который требовался от него.
Это где сопоставление с образцом в конструкторе, который включает эту информацию о равенстве типовэто важно.При сопоставлении с Just Refl :: Maybe (String :~: String)
Haskell не нужно, чтобы это совпадающее выражение имело подмножество типа (Typeable a, Read a) => String -> Maybe a
, ему просто нужно, чтобы оно было надмножеством String -> Maybe String
(подмножество исходного требуемого типа), чтоэто, будучи a -> Maybe a
.