Ticket #3734 (closed bug: fixed)

Opened 2 years ago

Last modified 2 years ago

overlapping orphan instances behave like incoherent without warning/error

Reported by: Liskni_si Owned by:
Priority: normal Milestone:
Component: Compiler (Type checker) Version: 6.10.4
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Incorrect result at runtime Difficulty:
Test Case: Blocked By:
Blocking: Related Tickets:

Description

Consider these three modules:

module A where

class (Show a) => A a

data A' = A' deriving (Show)
instance A A'

data A'' = A'' deriving (Show)
instance A A''

print_a :: (A a) => a -> IO ()
print_a a = print a
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
module B where

import A

data B a = B a deriving (Show)
instance (A a) => A (B a)
{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
module Main where

import A
import B

instance Show (B A') where
    show _ = "kokodak"

instance Show (B A'') where
    show _ = "brekeke"

instance A (B A'')

main :: IO ()
main = do
    print   (B A')
    print_a (B A')
    putStrLn ""
    print   (B A'')
    print_a (B A'')

Without understanding a thing about dictionaries, I would expect that if this actually compiles (which I now understand it should not), I'd get "kokodak kokodak brekeke brekeke" as output, but I got "kokodak B A' brekeke brekeke" instead.

I figured that even though I redefined Show (B A'), the A (B a) instance was defined in module B and consisted of the original Show dictionary. If I move the Show (B A') instance to module B, ghc complains that the definition of A (B a) depends on the instatiation of a and refuses to compile it, unless I enable IncoherentInstances.

The problem here is that if the Show (B A') instance is orphan, I get the IncoherentInstances behaviour for free without any warning or error, giving me the false feeling that the code is actually OK. Is it possible that ghc gives an error in this case, and may the documentation mention that Overlapping + Orphan => Incoherent?

Change History

Changed 2 years ago by simonpj

  • status changed from new to closed
  • resolution set to fixed

Good point. But it's worse than that. You can get incoherence from overlap without even orphans. I've added an explanation to the user guide, reproduced below:

Warning: overlapping instances must be used with care.  They 
can give rise to incoherence (ie different instance choices are made
in different parts of the program) even without <option>-XIncoherentInstances</option>. Consider:
<programlisting>
{-# LANGUAGE OverlappingInstances #-}
module Help where

    class MyShow a where
      myshow :: a -> String

    instance MyShow a => MyShow [a] where
      myshow xs = concatMap myshow xs

    showHelp :: MyShow a => [a] -> String
    showHelp xs = myshow xs

{-# LANGUAGE FlexibleInstances, OverlappingInstances #-}
module Main where
    import Help

    data T = MkT

    instance MyShow T where
      myshow x = "Used generic instance"

    instance MyShow [T] where
      myshow xs = "Used more specific instance"

    main = do { print (myshow [MkT]); print (showHelp [MkT]) }
</programlisting>
In function <literal>showHelp</literal> GHC sees no overlapping
instances, and so uses the <literal>MyShow [a]</literal> instance
without complaint.  In the call to <literal>myshow</literal> in <literal>main</literal>,
GHC resolves the <literal>MyShow [T]</literal> constraint using the overlapping
instance declaration in module <literal>Main</literal>. As a result, 
the program prints
<programlisting>
  "Used more specific instance"
  "Used generic instance"
</programlisting>
(An alternative possible behaviour, not currently implemented, 
would be to reject module <literal>Help</literal>
on the grounds that a later instance declaration might overlap the local one.)
</para>

Simon

Changed 2 years ago by Liskni_si

Awesome, thank you for the quick fix. :-)

Note: See TracTickets for help on using tickets.