Haha. Would that it were so.
I had almost exactly the same class in my own project, with the same instances justified by the same reasoning:
class x <: xs where index :: Index x xs
instance {-# INCOHERENT #-} x <: (x:xs) where index = Z
instance {-# OVERLAPPABLE #-} x <: xs => x <: (y:xs) where index = S index
I used this class to infer from types in context which element to pull from a heterogeneous list of skolem-tagged types. The result was a frightening heisenbug that violated the guarantees of my carefully crafted types and stole days of my life.
The core issue is (in my opinion) a GHC bug, or more charitably (and I can see the argument) an unfortunate deficiency in GHC’s treatment of our unrestricted types. We use polymorphic continuations like (forall x. ...) -> ...
to introduce new, unique types into a limited scope, but from GHC’s perspective we’ve simply accepted a polymorphic argument and neglected to supply it with a concrete type.
To follow the principle of least surprise, GHC should instantiate each such case at a unique type—if it must instantiate them at all. But it does the exact opposite: they’re all defaulted to Any
. Now instead of e1 :> [e1, e2]
and e2 :> [e1, e2]
, you have two dictionaries for Any :> [Any, Any]
and a specialiser that considers them interchangeable. Disaster.
One workaround is to NOINLINE
everything that introduces a type you want to be treated as unique—this apparently prevents the specialiser from witnessing e1 ~ Any ~ e2
. It also obstructs optimisation and could seriously degrade performance.
Another is to manually generate and supply the unique types yourself. There’s apparently a runExists
primitive in the wings for just this purpose (GHC#19675), but it’s not implemented yet. I’ve also had a go at it myself. See GHC#24924 for details.