
Simon Peyton-Jones
writes: I've realised that GHC's -XIncoherentInstances flag is, I think, over-conservative.
Hi Simon, by coincidence I've just come across a very similar issue with overlapping instances and FunDeps (following-up some discussion with Oleg, MartinS, EdwardK). Starting with: class C a b | a -> b where foo :: a -> b instance C [a] [a] where foo = id t1 = foo "a" -- t1 :: [Char] -> [Char] I then added: instance C [Char] [Char] where foo = id -- more specific! t2 = foo "a" -- t2 :: C [a] [a] => [a] -> [a] -- more polymorphic! My (probably subjective) experience is that somewhere around 2006 when Type Families started appearing, there were some subtle changes around overlaps. TF's seemed to be very pre-occupied with supporting 'coincident' (or confluent) _partial_ overlap. (By partial overlap I mean: some substitutions match both instances, some only one, some only t'other. This seems to be the root of the issue with Joachim's post on ghc-devs.) Partial overlaps are always going to be awkward for IncoherentInstances. With Type Families, you can always check that the result is confluent. But for class instances there's no such algebraic/semantic check possible. It's easy to avoid partial overlaps: just define an instance for the mgu of the overlap; then both of the less-specific instances totally overlap it. With the benefit of hindsight, I would have banned partial overlaps. IMO we could then validate class instance 'eagerly' at point of declaration (as Hugs does), rather than paying the penalty later with hazy/lazy instance selection woes. I strenuously try to avoid needing IncoherentInstances. I've no objection to your proposed "liberalise a bit".
Incidentally, I think it'd be an improvement to localise the
instance declarations, via pragmas, something like instance C [a] where {-# ALLOW_OVERLAP #-} op x = ....
Similarly {-# ALLOW_INCOHERENT #-}. Having -XOverlappingInstances for
and might be missed when looking at an instance. How valuable would
Overlapping/Incoherent flags to particular the whole module is a bit crude., this be?
+1 I strongly support localising these flags. Very seldom do I want Overlapping for every class in a module, and I'd rather the compiler told me if I inadvertently did overlap an instance. Better still, can we do {-# DISALLOW_PARTIAL #-} ?