I have the following type class and instances:
class StatType a where
toDouble :: a -> Double
instance StatType Double where
toDouble = id
instance Integral a => StatType a where
toDouble = fromIntegral
avg :: StatType a => [a] -> Double
avg = undefined
But then the expression
*Example> avg ([1,2,3,4] :: [Double])
reports a type error regarding overlapped instances
Overlapping instances for StatType Double
arising from a use of `avg'
Matching instances:
instance StatType Double -- Defined at Example.hs:61:10
instance Integral a => StatType a -- Defined at Example.hs:63:10
The type system cannot select between these two instances. However, Double
is not an Integral
type.
*Example> :i Double
data Double = GHC.Types.D# GHC.Prim.Double#
-- Defined in `GHC.Types'
instance StatType Double -- Defined at Example.hs:
instance Enum Double -- Defined in `GHC.Float'
instance Eq Double -- Defined in `GHC.Classes'
instance Floating Double -- Defined in `GHC.Float'
instance Fractional Double -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'
instance Ord Double -- Defined in `GHC.Classes'
instance Read Double -- Defined in `GHC.Read'
instance Real Double -- Defined in `GHC.Float'
instance RealFloat Double -- Defined in `GHC.Float'
instance RealFrac Double -- Defined in `GHC.Float'
instance Show Double -- Defined in `GHC.Float'
And I don't think Integral
implied by one of these or anything?
fromIntegral (3 :: Double)
raises a type error since Double
isn't an Integral
instance.
Why are these overlapped?
Thanks!
Quite simply, this is just how GHC works
When GHC tries to resolve, say, the constraint C Int Bool, it tries to match every instance declaration against the constraint, by instantiating the head of the instance declaration. Consider these declarations:
instance context1 => C Int a where ... -- (A) instance context2 => C a Bool where ... -- (B)
GHC's default behaviour is that exactly one instance must match the constraint it is trying to resolve. For example, the constraint C Int Bool matches instances (A) and (B), and hence would be rejected; while C Int Char matches only (A) and hence (A) is chosen.
Notice that
- When matching, GHC takes no account of the context of the instance declaration (context1 etc).
So ghc doesn't see
instance StatType Double
instance Integral a => StatType a
It sees
instance StatType Double
instance StatType a
Which are evidently overlapping.
The reason this works the way it does is that type classes are open. There is no instance for Integral Double
now but someone may import your library and declare one, at which point the instances would be overlapping even if the context was checked. Even worse, there would be no sensible way to prefer one over the other.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With