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
.
Num is a typeclass — a group of types — which includes all types which are regarded as numbers. The (Num a) => part of the signature restricts a to number types – or, in Haskell terminology, instances of Num .
An instance of a class is an individual object which belongs to that class. In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class.
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.
What should sum [] :: Vec3
return?
The sum
function could be defined as
sum :: (Num a) => [a] -> a
sum = foldl (+) 0
The 0
there is actually fromInteger (0 :: Integer)
, thus you need fromInteger
to use sum
.
Actually in base-4.8 sum
is defined in terms of Sum
Monoid
and Foldable
, but that's different story. You still need fromInteger (0 :: Integer)
there.
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