[GHC] #10270: inconsistent semantics of type class instance visibility outside recursive modules
GHC
ghc-devs at haskell.org
Wed Apr 8 18:12:01 UTC 2015
#10270: inconsistent semantics of type class instance visibility outside recursive
modules
-------------------------------------+-------------------------------------
Reporter: skilpat | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 7.10.1
Keywords: | Operating System: MacOS X
Architecture: x86_64 | Type of failure: GHC rejects
(amd64) | valid program
Test Case: | Blocked By:
Blocking: | Related Tickets:
Differential Revisions: |
-------------------------------------+-------------------------------------
When you have an instance defined in a module that's part of a recursive
module loop, when should modules outside the loop see it? Right now, GHC
behaves differently depending on whether it's in batch or in single-shot
compilation mode. Which one is correct? Dunno!
Here's the example (code at the bottom). The gist is that you have two
modules A and B that define data types T and U, respectively, which depend
on each other, so A and B are recursive modules and we need a boot file --
say, for A.
Now suppose we define an instance Eq T in the implementation of A but not
in the boot file. B imports the boot file, so it doesn't know about this
instance. And then I have some third module, Main, outside the loop, which
imports only B. And now the central question: ''Does Main know about the
Eq T defined in A?''
We can test how GHC answers this question by defining an (orphan) instance
for Eq T in Main. In batch compilation mode, Main is rejected for defining
a duplicate instance. In single-shot compilation mode, however, Main is
accepted and any equality test in Main uses the locally defined instance;
i.e., B doesn't know about A's Eq T and so neither does Main.
So which is correct? If you ask me, the latter semantics is correct, but I
can see why the former might be argued as well (e.g., according to the
fixed-point semantics of import/export described in (1)). In any case, the
semantics between the two compilation modes should probably agree, right?
{{{#!hs
-- A.hs-boot
module A where
data T -- a mutually recursive data type, along with B.U
t :: T -- some value to test for Eq in Main
-- B.hs
module B(module A, module B) where
-- export A.{T,t} since Main doesn't import A
import {-# SOURCE #-} A
-- mutually recursive data type across modules
data U = U | UT T
-- A.hs
module A where
import B
-- mutually recursive data type across modules
data T = T | TU U
-- the true instance
instance Eq T where
_ == _ = True
-- some value to test Eq instance in Main
t :: T
t = T
-- Main.hs
module Main where
import B -- no import of A
-- an orphan instance for Eq T.
-- okay in one-shot mode; not okay in batch (--make) mode
instance Eq T where
_ == _ = False
-- in one-shot mode, this prints False
main = putStrLn $ show $ t == t
}}}
Commands and output for batch mode:
{{{
$ ghc --make Main
[1 of 4] Compiling A[boot] ( A.hs-boot, A.o-boot )
[2 of 4] Compiling B ( B.hs, B.o )
[3 of 4] Compiling A ( A.hs, A.o )
[4 of 4] Compiling Main ( Main.hs, Main.o )
A.hs:8:10:
Duplicate instance declarations:
instance Eq T -- Defined at A.hs:8:10
instance Eq T -- Defined at Main.hs:7:10
}}}
Commands and output for single-shot mode:
{{{
$ ghc -c A.hs-boot
$ ghc -c B.hs
$ ghc -c A.hs
$ ghc -c Main.hs
$ ghc A.o B.o Main.o -o main
$ ./main
False
}}}
Tested on GHC 7.6.3, 7.8.3, and 7.10.1.
(1) ''A Formal Specification for the Haskell 98 Module System''. Iavor S.
Diatchki, Mark P. Jones, and Thomas Hallgren. Haskell '02.
http://web.cecs.pdx.edu/~mpj/pubs/hsmods.pdf
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10270>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list