UndecidableSuperClasses
делает не делает разрешение экземпляра ленивым.Компилятор по-прежнему будет расширять ограничения суперкласса, насколько это возможно.Я считаю, что поля в словарях экземпляров, которые указывают на словари суперкласса, строгие , и GHC фактически связывает их во время компиляции.Это отличается от UndecidableInstances
, который позволяет ограничения экземпляров обрабатывать (немного) лениво.
deriving instance Show (f (Fix f)) => Show (Fix f)
будет работать просто отлично.При разрешении экземпляра, например, Show (Fix Maybe)
), GHC увидит, что ему нужно Show (Maybe (Fix Maybe))
.Затем он видит, что ему нужно Show (Fix Maybe)
(что он в настоящее время разрешает), и принимает, что благодаря UndecidableInstances
.
Все, что делает UndecidableSuperClases
, отключает проверки, гарантирующие, что расширение не будет зацикливаться.Смотрите бит в начале выступления Эда Кметта , где он описывает процесс «достижения фиксированной точки».
Рассмотрим рабочий пример (извлечено из Data.Constraint.Forall
):
type family Skolem (p :: k -> Constraint) :: k
class p (Skolem p) => Forall (p :: k -> Constraint)
GHC принимает это только с UndecidableSuperclasses
.Зачем?Потому что он ничего не знает о том, что может означать это ограничение.Насколько он знает, это может быть случай, когда p (Skolem p)
уменьшится до Forall p
.И это действительно может произойти!
class Forall P => P x
-- This definition loops the type checker
foo :: P x => x
foo = undefined