GHC: Ticket #1549: ghc derives different type signature for equal functions with overlapping instances
http://ghc.haskell.org/trac/ghc/ticket/1549
<p>
Insert "This flag does not change ghc's behaviour when no verlapping instances are present." after "The -fallow-overlapping-instances lag instructs GHC to allow more than one instance to match, provided here is a most specific one." in <a href="http://www.haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html#instance-overlap">http://www.haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html#instance-overlap</a>
</p>
<p>
(old description follows for reference)
</p>
<p>
The code below derives two different type signatures for the same function in two different modules.
</p>
<p>
One of these modules defines a new instance for the <tt>Monad</tt> class. What's interesting is that this causes the type checker to derive a more general type than before. I think that's a bug.
</p>
<p>
Another interesting point is that the code below works without -fallow-overlapping-instances,
but if the instances from <tt>A.hs</tt> and from <tt>B.hs</tt> are combined in a single file, the compiler complains very loudly. The derived types are unaffected by <tt>-fallow-overlapping-instances</tt>.
</p>
<pre class="wiki">==> A.hs <==
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module A (MaybeT (..), module Control.Monad) where
import Control.Monad
newtype MaybeT m a = MaybeT {runMaybeT :: m (Maybe a)}
instance Monad m => Monad (MaybeT m)
==> B.hs <==
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module B (qqq) where
import A
data Foo a = Foo
instance Monad (MaybeT Foo) where
qqq _ = runMaybeT (runMaybeT (return 1) >> return 2)
==> C.hs <==
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module C (rrr) where
import A
rrr _ = runMaybeT (runMaybeT (return 1) >> return 2)
==> D.hs <==
module D (qqq, rrr) where
import B
import C
{-
# ghci -fallow-overlapping-instances D.hs
GHCi, version 6.7.20070716: http://www.haskell.org/ghc/ :? for help
(...)
Ok, modules loaded: C, D, A, B.
*D> :t qqq
qqq :: (Monad (A.MaybeT m), Num t1) => t -> m (Maybe t1)
*D> :t rrr
rrr :: (Monad m, Num t1) => t -> m (Maybe t1)
*D> :i MaybeT
newtype MaybeT m a = MaybeT {runMaybeT :: m (Maybe a)}
-- Defined at A.hs:6:8-13
instance [overlap ok] (Monad m) => Monad (MaybeT m)
-- Defined at A.hs:8:0-35
-}
</pre><p>
I think that the type derived for <tt>qqq</tt> is correct in the presence of overlapping instances.
</p>
en-usGHChttp://ghc.haskell.org/trac/ghc/chrome/site/ghc_logo.png
http://ghc.haskell.org/trac/ghc/ticket/1549
Trac 1.0.9simonpjTue, 31 Jul 2007 13:53:53 GMTstatus changed; resolution set
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:1
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:1
<ul>
<li><strong>status</strong>
changed from <em>new</em> to <em>closed</em>
</li>
<li><strong>resolution</strong>
set to <em>invalid</em>
</li>
</ul>
<p>
The rules for instance overlap are described here: <a href="http://www.haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html#instance-overlap">http://www.haskell.org/ghc/docs/latest/html/users_guide/type-extensions.html#instance-overlap</a>
</p>
<p>
When compiling B.hs, GHC sees that there is an overlapping instance and so refrains from simplifying the constraint <tt>Monad (A.MaybeT m)</tt>, becuase <tt>m</tt> might be instantiated to <tt>Foo</tt>. But when compiling C.hs, it sees no such overlap, so it simplifies the constraint. It doesn't see the overlap because B is not in the transitive imports of C.
</p>
<p>
That's why you get different types. To get the same types, we'd have to refrain from <strong>ever</strong> using an instance declaration in case it was later overlapped, and that seems impractical becuase it would percolate all constraints up to <tt>main</tt>.
</p>
<p>
In short, this seems to be the documented behaviour. But the documentation may not be adequately clear. If you can suggest concrete improvements (E.g. Add ".." after "...") then that would be v helpful. Thanks.
</p>
<p>
Simon
</p>
Ticketint-eThu, 09 Aug 2007 14:38:08 GMTstatus, description, type, component, priority, severity changed; resolution deleted
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:2
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:2
<ul>
<li><strong>status</strong>
changed from <em>closed</em> to <em>reopened</em>
</li>
<li><strong>description</strong>
modified (<a href="/trac/ghc/ticket/1549?action=diff&version=2">diff</a>)
</li>
<li><strong>type</strong>
changed from <em>bug</em> to <em>proposal</em>
</li>
<li><strong>component</strong>
changed from <em>Compiler (Type checker)</em> to <em>Documentation</em>
</li>
<li><strong>priority</strong>
changed from <em>normal</em> to <em>low</em>
</li>
<li><strong>resolution</strong>
<em>invalid</em> deleted
</li>
<li><strong>severity</strong>
changed from <em>normal</em> to <em>trivial</em>
</li>
</ul>
<p>
Thank you.
</p>
<p>
The connection that I hadn't made is that type inference (in particular, choosing an instance to use for type classes for a monotype) and constraint simplification (which affects polymorphic types) are really the same thing.
</p>
<p>
Once that misconception is resolved the behaviour is just as expected. The documentation could be clearer about the fact that allowing overlapping instances does not change the behaviour for the case when none are present though.
</p>
<p>
Maybe inserting "This flag does not change ghc's behaviour when no overlapping instances are present." after "The -fallow-overlapping-instances flag instructs GHC to allow more than one instance to match, provided there is a most specific one." would clear that up?
</p>
<p>
Bertram
</p>
TicketsimonpjThu, 09 Aug 2007 14:54:48 GMT
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:3
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:3
<p>
Looking at it again, I this would be better to add this after "without complaining about
the problem of subsequent instantiations":
</p>
<p>
Notice that we gave a type signature to <tt>f</tt> so GHC had to
<em>check</em> that <tt>f</tt> has the specified type.
Suppose instead we do not give a type signature, asking GHC to <em>infer</em>
it instead. In this case, GHC will refrain from
simplifying the constraint <tt>C Int [Int]</tt> (for the same reason
as before) but, rather than rejecting the program, it will infer the type
</p>
<pre class="wiki"> f :: C Int b => [b] -> [b]
</pre><p>
That postpones the question of which instance to pick to the
call site for <tt>f</tt>
by which time more is known about the type <tt>b</tt>
</p>
Ticketint-eSat, 18 Aug 2007 23:11:29 GMT
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:4
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:4
<p>
I forgot to follow up on this last week. Your proposed change sounds good to me, it addresses exactly the problem that I had.
</p>
TicketsimonpjMon, 20 Aug 2007 21:04:08 GMTstatus changed; resolution set
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:5
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:5
<ul>
<li><strong>status</strong>
changed from <em>reopened</em> to <em>closed</em>
</li>
<li><strong>resolution</strong>
set to <em>fixed</em>
</li>
</ul>
<p>
OK, I've committed the documentation fix, and am closing the bug. Thanks for your help.
</p>
<p>
Simon
</p>
TicketrossSat, 25 Aug 2007 00:28:42 GMTtype changed
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:6
http://ghc.haskell.org/trac/ghc/ticket/1549#comment:6
<ul>
<li><strong>type</strong>
changed from <em>proposal</em> to <em>bug</em>
</li>
</ul>
Ticket