GHC: Ticket #7296: ghc-7 assumes incoherent instances without requiring language `IncoherentInstances`
http://ghc.haskell.org/trac/ghc/ticket/7296
<p>
the attached examples works with ghc-7 and returns
</p>
<pre class="wiki"> *Main> a
[Spec1 Spec2]
*Main> b
[]
</pre><p>
(One may wish that b also returned [Spec1 Spec2])
</p>
<p>
ghc-6 complains with
</p>
<pre class="wiki">Splittable.hs:16:36:
Overlapping instances for Test a Spec2
arising from a use of `test' at Splittable.hs:16:36-43
Matching instances:
instance [overlap ok] Test a Spec2
-- Defined at Splittable.hs:20:13-24
instance [overlap ok] Test Bool Spec2
-- Defined at Splittable.hs:25:13-27
(The choice depends on the instantiation of `a'
To pick the first instance above, use -XIncoherentInstances
when compiling the other instance declarations)
In the second argument of `($)', namely `test a b'
In the expression: map Spec1 $ test a b
In the definition of `test':
test a (Spec1 b) = map Spec1 $ test a b
Failed, modules loaded: none.
</pre><p>
After adding <tt>IncoherentInstances</tt> the file also goes through ghc-6 with the same results.
</p>
en-usGHChttp://ghc.haskell.org/trac/ghc/chrome/site/ghc_logo.png
http://ghc.haskell.org/trac/ghc/ticket/7296
Trac 1.0.1maederThu, 04 Oct 2012 12:50:46 GMTattachment set
http://ghc.haskell.org/trac/ghc/ticket/7296
http://ghc.haskell.org/trac/ghc/ticket/7296
<ul>
<li><strong>attachment</strong>
set to <em>Splittable.hs</em>
</li>
</ul>
TicketmaederThu, 04 Oct 2012 13:30:24 GMT
http://ghc.haskell.org/trac/ghc/ticket/7296#comment:1
http://ghc.haskell.org/trac/ghc/ticket/7296#comment:1
<p>
I meant, one may wish that a and b returned equal results, namely that a returned "[]" by using the more special instance of Spec2 in the instance definition of Spec1.
</p>
TicketsimonpjThu, 04 Oct 2012 13:57:17 GMTdifficulty set
http://ghc.haskell.org/trac/ghc/ticket/7296#comment:2
http://ghc.haskell.org/trac/ghc/ticket/7296#comment:2
<ul>
<li><strong>difficulty</strong>
set to <em>Unknown</em>
</li>
</ul>
<p>
You are right, and this is tricky. The real error is that in the recursive call to <tt>test</tt> on line 16 of <tt>Splittable.hs</tt>, rather than rejecting the code (which is arguably right) GHC picks the <tt>Test a Spec2</tt> instance on line 20.
</p>
<p>
But that's actually deliberate. Here the note from <tt>TcInstDcls</tt>:
</p>
<pre class="wiki">Note [Subtle interaction of recursion and overlap]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Consider this
class C a where { op1,op2 :: a -> a }
instance C a => C [a] where
op1 x = op2 x ++ op2 x
op2 x = ...
instance C [Int] where
...
When type-checking the C [a] instance, we need a C [a] dictionary (for
the call of op2). If we look up in the instance environment, we find
an overlap. And in *general* the right thing is to complain (see Note
[Overlapping instances] in InstEnv). But in *this* case it's wrong to
complain, because we just want to delegate to the op2 of this same
instance.
Why is this justified? Because we generate a (C [a]) constraint in
a context in which 'a' cannot be instantiated to anything that matches
other overlapping instances, or else we would not be excecuting this
version of op1 in the first place.
It might even be a bit disguised:
nullFail :: C [a] => [a] -> [a]
nullFail x = op2 x ++ op2 x
instance C a => C [a] where
op1 x = nullFail x
Precisely this is used in package 'regex-base', module Context.hs.
See the overlapping instances for RegexContext, and the fact that they
call 'nullFail' just like the example above. The DoCon package also
does the same thing; it shows up in module Fraction.hs
Conclusion: when typechecking the methods in a C [a] instance, we want to
treat the 'a' as an *existential* type variable, in the sense described
by Note [Binding when looking up instances]. That is why isOverlappableTyVar
responds True to an InstSkol, which is the kind of skolem we use in
tcInstDecl2.
</pre><p>
But your example points out that my reasoning is flawed. I'm not sure exactly what to do, and I'm too swamped to think about it right now. I hope it's not a show stopper for you.
</p>
<p>
You can work around thus:
</p>
<pre class="wiki">instance Test a Spec2 => Test a Spec1 where
test a (MkSpec1 b) = map MkSpec1 $ test a b
</pre><p>
which defers the choice to later, which is what we want. Then you get <tt>[]</tt> for both tests. Sadly you need <tt>FlexibleContexts</tt> and <tt>UndecidableInstances</tt> to persuade GHC to accept this instance.
</p>
<p>
Simon
</p>
TicketmaederThu, 04 Oct 2012 14:30:00 GMT
http://ghc.haskell.org/trac/ghc/ticket/7296#comment:3
http://ghc.haskell.org/trac/ghc/ticket/7296#comment:3
<p>
Thanks for this analysis, it is no show stopper for us.
</p>
TicketiglooFri, 12 Apr 2013 14:58:10 GMTcomponent changed; owner, milestone set
http://ghc.haskell.org/trac/ghc/ticket/7296#comment:4
http://ghc.haskell.org/trac/ghc/ticket/7296#comment:4
<ul>
<li><strong>owner</strong>
set to <em>simonpj</em>
</li>
<li><strong>component</strong>
changed from <em>Compiler</em> to <em>Compiler (Type checker)</em>
</li>
<li><strong>milestone</strong>
set to <em>7.8.1</em>
</li>
</ul>
Ticket