4

Trying to generalise (+) to more than just Nums, I wrote a up an Addable class:

{-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-}

class Addable a where
  (+) :: Addable a => a -> a -> a

instance Addable [a] where
  (+) = (++)

instance Num a => Addable a where
  (+) = (Prelude.+)

When trying to add (concatenate) lists, GHC complains about overlapping instances:

*Test> "abc" + "defghi"

<interactive>:84:7:
    Overlapping instances for Addable [Char] arising from a use of `+'
    Matching instances:
      instance Num a => Addable a -- Defined at Utils.hs:23:10
      instance Addable [a] -- Defined at Utils.hs:20:10
    In the expression: "abc" + "defghi"
    In an equation for `it': it = "abc" + "defghi"

I know that GHC disregards context when choosing instances of typeclasses, so trying to choose between Addable [a] and Addable a is indeed a problem. However, I expect GHC to choose the first definition since it is more specific. Why isn't that happening?

Furthermore, is there an elegant workaround for this problem? Or am I going at this from the wrong angle?

ThreeFx
  • 7,250
  • 1
  • 27
  • 51

1 Answers1

7

You need to enable overlapping instances in order to actually use them. In older versions of the compiler, you could do this per-module with the OverlappingInstances extension:

{-# LANGUAGE OverlappingInstances #-}

This works properly for this code:

λ> :set -XOverlappingInstances
λ> "abc" + "def"
"abcdef"

However, this approach is deprecated in newer versions of GHC (at least in 8.0):

Misc.hs:1:73-92: warning: …
    -XOverlappingInstances is deprecated: 
      instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS

So the more modern approach is to specify this on a per-instance basis:

instance {-# OVERLAPPABLE #-} Num a => Addable a where
  (+) = (Prelude.+)

This style of pragma also exists for OVERLAPPING, OVERLAPS and INCOHERENT, letting you annotate specific instances with these properties.

Tikhon Jelvis
  • 67,485
  • 18
  • 177
  • 214
  • I'll just sit here and facepalm myself using all hands available. How did I not see this? – ThreeFx Aug 29 '16 at 21:42
  • @ThreeFx: I figured out how to use the per-instance pragma. Also, I've run into this problem myself—it's weird to need an extension when I *use* an instance rather than when I define it! – Tikhon Jelvis Aug 29 '16 at 21:45