问题
Trying to generalise (+)
to more than just Num
s, 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?
回答1:
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.
来源:https://stackoverflow.com/questions/39215103/ghc-overlapping-instances-when-generalising-addition