The following snippet makes GHC (checked with 8.6.2 & 8.4.4) stuck during compilation:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-}
import GHC.Exts (Constraint)
data T = T
type family F t (c :: * -> Constraint) :: Constraint
type instance F T c = c T
class F t C => C t where
t :: C T => t
t = undefined
I think that it gets stuck because for t
GHC tries to find C T
, which leads to F T C
which expands via type family F
back to C T
, which is what it was looking for (infinite loop).
I suppose that theoretically GHC could tell that it reached its quest for C T
from itself and that anything that depends on itself can work fine recursively, or am I misunderstanding something?
Side note: in the real example where I stumbled upon this behaviour I was able to achieve what I wanted without the compiler being stuck by replacing UndecidableSuperClasses
with Data.Constraint.Dict
instead.
UndecidableSuperClasses
does not make instance resolution lazy. The compiler will still expand superclass constraints as far as possible. I believe that the fields in instance dictionaries that point to the superclass dictionaries are strict, and GHC actually pins them down at compile time. This is in contrast toUndecidableInstances
, which allows instance constraints to be treated (a bit) lazily.will work just fine. When resolving an instance for, e.g.,
Show (Fix Maybe)
), GHC will see that it needsShow (Maybe (Fix Maybe))
. It then sees it needsShow (Fix Maybe)
(which it's currently resolving) and accept that thanks toUndecidableInstances
.All
UndecidableSuperClases
does is disable the checks that guarantee that expansion won't loop. See the bit near the beginning of Ed Kmett's talk where he describes the process "reaching a fixed point".Consider a working example (ripped from
Data.Constraint.Forall
):GHC only accepts this with
UndecidableSuperclasses
. Why? Because it doesn't know anything about what that constraint might mean. As far as it knows, it could be the case thatp (Skolem p)
will reduce toForall p
. And that could actually happen!