haskellnamespacestypeclassname-conflict

How to tell Haskell to not import the same instance from two modules?


I'm using the following typeclass:

module T where
class T a where
  v :: a

An instance of T Int that I implemented:

import T
import A (av)

instance T Int where
  v = 0

main = putStrLn (av ++ show v)

And a module that I want to use a value from, which also has an instance of T Int.

module A where
import T
instance T Int where
  v = 0
av = "value from A"

The problem is this doesn't work:

$ runghc Main.hs 

Main.hs:4:9:
    Duplicate instance declarations:
      instance T Int -- Defined at Main.hs:4:9-13
      instance T Int -- Defined at A.hs:3:9-13

Haskell complains that there are 2 declarations for the same instance. How can I tell him not to import the instance from B, or to unify both instances, or to only use the instance from Main?


Solution

  • Unfortunately, you can't control how instances get imported and exported; see Do Haskell imports have side effects?.

    This means you will have to refactor your code to ensure that the instance is only defined in one file. In general, it is best to only define an instance in the file that defines either the class or the data type--in fact, there is even a warning about "orphan" instances which don't follow this rule. (Look at Orphaned instances in Haskell for a long discussion about why you should avoid orphan instances.)

    However, if this is impossible for some reason, you can still just arbitrarily choose one of the files to keep it or even create a new module to be imported by all the files needing that particular instance.

    More generally, how would you deal with the possibility that the two instances did different things, like:

    instance T Int where v = 0
    {- And in a different file: -}
    instance T Int where v = 1
    

    There is really no immediately obvious way to disambiguate these two without significantly changing how the Haskell typeclass system works.

    Since you wrote one of the instances yourself, simply remove that one. Since it's the same as the predefined one, just import that module wherever you need to use it.