Why does the sum need GHC.Num.fromInteger?

advertisements

I have a 3d vector data type defined as 3 floats. I understand that if I provide a Num instance for my class and define the normal mathematical operators, I can use them on my class.

data Vec3 = Vec3 { x :: Float
                 , y :: Float
                 , z :: Float
                 } deriving (Show, Eq)

instance Num Vec3 where
  (+) v1 v2 = Vec3 (x v1 + x v2) (y v1 + y v2) (z v1 + z v2)

When I load my file into ghci, I get warnings because I didn't define all the functions in Num, which makes sense.

Prelude> :l temp.hs
[1 of 1] Compiling Main             ( temp.hs, interpreted )

temp.hs:6:10: Warning:
    No explicit method or default declaration for `*'
    In the instance declaration for `Num Vec3'

temp.hs:6:10: Warning:
    No explicit method or default declaration for `abs'
    In the instance declaration for `Num Vec3'

temp.hs:6:10: Warning:
    No explicit method or default declaration for `signum'
    In the instance declaration for `Num Vec3'

temp.hs:6:10: Warning:
    No explicit method or default declaration for `fromInteger'
    In the instance declaration for `Num Vec3'
Ok, modules loaded: Main.

However, I can still use the ones I've defined.

*Main> let a = Vec3 1.0 2.0 3.0
*Main> let b = Vec3 2.0 4.0 5.0
*Main> a + b
Vec3 {x = 3.0, y = 6.0, z = 8.0}

My confusion comes from the following error I get when trying to use the sum function

*Main> sum [a,b]
Vec3 {x = *** Exception: temp.hs:6:10-17: No instance nor default method for class operation GHC.Num.fromInteger

Why does sum need a fromInteger definition for my Vec3 data type? For one, I would have figured that sum only uses the + function, and for another, my data type doesn't use Integer.


Here's how sum is implemented:

sum = foldl (+) 0

Notice the 0 literal. Let's check it's type in GHCi:

λ> :t 0
0 :: Num a => a

As it turns out, numeric literals are sugar for fromInteger. Ie, 0 is actually fromInteger 0.

Thus, sum requires fromInteger, because the above definition is sugar for:

sum = foldl (+) (fromInteger 0)


The implementation of fromInteger is easy:

instance Num Vec3 where
    fromInteger n = let a = (fromInteger n) in Vec3 a a a

Furthermore, I would highly recommend that, whenever making an instance, always define it completely to avoid unforeseen trouble like this.