Per-class incoherence, and solve incoherent instances last
The behavior of type-inference and !OverlappingInstances has changed between GHC 6.12 and GHC 7.0 such that the following code type-checks under 6.12, but not 7.0rc2. I assume this change has something to do with the new type checker in GHC 7, but it is not clear to me if this change in behavior is intended. Nor am I clear how to achieve something similar to the old behavior. This is preventing HSP (and by extension, happstack) from migrating to GHC 7. I reported this earlier on the mailing lists, but I have further simplied the test case here.
> {-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FunctionalDependencies
> , FlexibleContexts, FlexibleInstances, UndecidableInstances
> , TypeSynonymInstances, GeneralizedNewtypeDeriving
> , OverlappingInstances
> #-}
> module XMLGenerator where
XMLGenT monad transformer -- not really sure offhand why we have this, but we do:
> newtype XMLGenT m a = XMLGenT (m a)
> deriving (Functor, Monad)
next we have a simple class for generating XML
> class Monad m => XMLGen m where
> type XML m -- ^ the type that will be used for the generated XML. Can be different on a per-monad basis
> data Child m -- ^ the type that will be used for children of an element
> genElement :: String -> XMLGenT m (XML m) -- ^ a function to generate an element with no children or attributes. e.g. <br />
A class specifying how to turn a value of type 'c' into a list of Child elements
> class XMLGen m => EmbedAsChild m c where
> asChild :: c -> XMLGenT m [Child m]
Next we have an instance of !EmbedAsChild that embeds the XMLGenT wrapped 'c' value into 'm' by removing the XMLGenT wrapper, (implementation removed since it does not affect the error):
> instance (EmbedAsChild m c, m1 ~ m) => EmbedAsChild m (XMLGenT m1 c)
Now we have an instance which embeds 'XML' as a child. The specific type that is considered 'XML' is dependent on the XMLGen instance for 'm' (body also removed):
> instance (XMLGen m, XML m ~ x) => EmbedAsChild m x
Next we define the Identity monad transformer and make it an instance of Monad and XMLGen:
> data Xml = Xml
> data IdentityT m a = IdentityT (m a)
> instance Monad (IdentityT m)
> instance XMLGen (IdentityT m) where
> type XML (IdentityT m) = Xml
And we also define the Identity monad:
> data Identity a = Identity a
> instance Monad Identity
Here we specify a way to embed (XMLGenT Identity ()) into IdentityT IO:
> instance EmbedAsChild (IdentityT IO) (XMLGenT Identity ())
Next we define some arbitrary type that we want to convert to xml:
> data FooBar = FooBar
And now we try to create a more specific !EmbedAsChild instance:
> instance EmbedAsChild (IdentityT IO) FooBar where
> asChild b = asChild $ (genElement "foo")
Under GHC 6.12, everything type-checks fine. But under GHC 7 we get the error:
Prelude> :load "../XMLGenerator.lhs"
[1 of 1] Compiling XMLGenerator ( ../XMLGenerator.lhs, interpreted )
../XMLGenerator.lhs:64:17:
Overlapping instances for EmbedAsChild
(IdentityT IO) (XMLGenT m (XML m))
arising from a use of `asChild'
Matching instances:
instance [overlap ok] (m1 ~ m, EmbedAsChild m c) =>
EmbedAsChild m (XMLGenT m1 c)
-- Defined at ../XMLGenerator.lhs:31:12-70
(The choice depends on the instantiation of `m'
To pick the first instance above, use -XIncoherentInstances
when compiling the other instance declarations)
In the expression: asChild
In the expression: asChild $ (genElement "foo")
In an equation for `asChild':
asChild b = asChild $ (genElement "foo")
Failed, modules loaded: none.
I can make the error go away by specifying the type signature of genElement explicitly:
] instance EmbedAsChild (IdentityT IO) FooBar where
] asChild b = asChild $ ((genElement "foo") :: XMLGenT (IdentityT IO) Xml)
But I would need to do this in dozens of places. (possibly hundreds). So that is not very pleasing.
Removing this instance also makes the error go away:
] instance EmbedAsChild (IdentityT IO) (XMLGenT Identity ())
But 'XMLGenT Identity ()' is not a type that genElement can have in that context.. Plus I need that instance..
Adding !IncoherentInstances changes the error:
Prelude> :load "../XMLGenerator.lhs"
[1 of 1] Compiling XMLGenerator ( ../XMLGenerator.lhs, interpreted )
../XMLGenerator.lhs:64:28:
Couldn't match type `XMLGenT m (XML m)' with `Xml'
In the second argument of `($)', namely `(genElement "foo")'
In the expression: asChild $ (genElement "foo")
In an equation for `asChild':
asChild b = asChild $ (genElement "foo")
Failed, modules loaded: none.
I have attached a literate haskell version of this bug report that you can test with. Thanks!