Странная программа, которая требует несвязных экземпляров, но всегда, кажется, выбирает "правильный"?
Рассмотрим следующую программу, которая компилируется только с включенными некогерентными экземплярами:
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-}
{-# LANGUAGE IncoherentInstances #-}
main = do
print (g (undefined :: Int))
print (g (undefined :: Bool))
print (g (undefined :: Char))
data True
class CA t where
type A t; type A t = True
fa :: t -> String
instance CA Int where
fa _ = "Int"
class CB t where
type B t; type B t = True
fb :: t -> String
instance CB Bool where
fb _ = "Bool"
class CC t where
type C t; type C t = True
fc :: t -> String
instance CC Char where
fc _ = "Char"
class CAll t t1 t2 t3 where
g :: (t1 ~ A t, t2 ~ B t, t3 ~ C t) => t -> String
instance (CA t) => CAll t True t2 t3 where g = fa
instance (CB t) => CAll t t1 True t3 where g = fb
instance (CC t) => CAll t t1 t2 True where g = fc
При компиляции без несвязных экземпляров он утверждает, что несколько экземпляров совпадают. Казалось бы, это подразумевает, что когда допускаются несвязные экземпляры, экземпляры будут выбираться произвольно.
Если мне не повезет, это приведет к ошибкам компиляции, поскольку ограничения экземпляров в большинстве случаев не будут выполнены.
Но с несвязными экземплярами я не получаю ошибок компиляции и действительно получаю следующий вывод с выбранными "правильными" экземплярами:
"Int"
"Bool"
"Char"
Так что я могу только заключить одну из следующих вещей:
- GHC отслеживает сбои контекста экземпляра (что-то, что он говорит, что не делает в своей собственной документации)
- GHC на самом деле знает, что есть только один соответствующий экземпляр, но не достаточно смел, чтобы использовать его, если не включить непоследовательные экземпляры
- Мне просто очень повезло (1 из 33 = 27 шансов)
- Что-то еще происходит.
Я подозреваю, что ответ 4 (возможно, в сочетании с 2). Я хотел бы, чтобы любой ответ объяснил, что здесь происходит, и насколько я могу положиться на это поведение в несвязных случаях? Если это надежно, кажется, я мог бы использовать это поведение для создания довольно сложных иерархий классов, которые на самом деле ведут себя как подтипы, например, я мог бы сказать, что все типы класса A и класса B находятся в классе C, и средство записи экземпляра может сделать экземпляр для A без необходимости явно делать экземпляр для C.
Редактировать:
Я подозреваю, что ответ как-то связан с этим в документах GHC:
- Найти все экземпляры I, которые соответствуют целевому ограничению; то есть целевое ограничение является экземпляром подстановки I. Эти объявления экземпляра являются кандидатами.
- Исключите любого кандидата IX, для которого выполняются оба следующих условия:
- Есть другой кандидат IY, который является более конкретным; то есть IY является экземпляром замещения IX, но не наоборот.
- Либо IX перекрывается, либо IY перекрывается. (Этот дизайн "или / или", а не "оба / и", позволяет клиенту преднамеренно переопределять экземпляр из библиотеки, не требуя изменения в библиотеке.)
Если остается только один некогерентный кандидат, выберите его. Если все остальные кандидаты несвязны, выберите произвольного. В противном случае поиск завершается неудачно (т. Е. Когда более одного выжившего кандидата не является непоследовательным).
Если выбранный кандидат (из предыдущего шага) является непоследовательным, поиск завершается успешно, возвращая этого кандидата.
Если нет, найдите все экземпляры, которые объединяются с целевым ограничением, но не совпадают с ним. Такие экземпляры, не являющиеся кандидатами, могут совпадать при дальнейшем создании целевого ограничения. Если все они непоследовательны, поиск завершается успешно, возвращая выбранного кандидата; если нет, поиск не удался.
Поправьте меня, если я ошибаюсь, но независимо от того, выбраны несогласованные экземпляры или нет, на первом шаге есть только один экземпляр, который "соответствует". В некогерентном случае мы выпадаем с этим "согласованным" случаем на шаге 4. Но в некогерентном случае мы переходим к шагу 4, и хотя только один экземпляр "совпадает", мы находим другие экземпляры "унифицированными", Поэтому мы должны отказаться.
Это понимание правильно?
И если да, то может ли кто-нибудь точно объяснить, что означают слова "совпадение" и "унификация", и разницу между ними?