Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overlapping instances for between Double and Integral types

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!

like image 561
Tim Avatar asked Mar 20 '23 14:03

Tim


1 Answers

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.

like image 130
user2407038 Avatar answered Apr 27 '23 00:04

user2407038