labrynth
labrynth

Reputation: 155

Type Signatures in Haskell

My question is regarding Type Signatures.

The following code complies:

data Vector a = Vector a a a deriving (Show) 
vMult :: (Num a) => Vector a -> a -> Vector a 
(Vector i j k) `vMult` m = Vector (i*m) (j*m) (k*m)

However, I do not understand why substituting the above type signature (on line number 2) with the following does not work:

vMult :: (Num a) => Vector a -> Num -> Vector a  

My understanding is that since m is of type Num (eg. the number 8), and since i, j, k are Num as well, there should be no problems with computing Vector (i*m) (j*m) (k*m).

Kindly correct my understanding.

Upvotes: 3

Views: 371

Answers (3)

shree.pat18
shree.pat18

Reputation: 21757

Multiplication is defined on Num a and Num a i.e. 2 arguments of same Type belonging to Num Typeclass.

(*) :: (Num a) => a -> a -> a 

If you only have Num in your type signature, it would be akin to asking to allow multiplication to be done between say an Integer and Float because both are Types belonging to Typeclass Num. That is why you need to to specify the Type that should be used amd not just the Typeclass.

There is already a constraint to ensure that a should belong to Num when you use it for your Vector. In order to multiply each element in your Vector with the chosen scalar, that scalar must not only belong to Num but also be the same type i.e. a.

Upvotes: 2

AndrewC
AndrewC

Reputation: 32455

Num a isn't actually a type at all

Num is a type class, so if I say Num a it means a is a number type, so then

vMult :: (Num a) => Vector a -> a -> Vector a

means "as long as a is a number type, vMult takes a Vector of as and a single a and returns a Vector of as."

That means that vMult can work like
vMult :: Vector Int -> Int -> Vector Int or
vMult :: Vector Double -> Double -> Vector Double.
Notice you get each type by replacing all the as in the original with a single number type.

Num on it's own doesn't make sense

Num on its own would mean "is a number type", so if your function had the type signature

vMult :: (Num a) => Vector a -> Num -> Vector a

it would read "as long as a is a number type, vMult takes a Vector of as and an is a number type and returns a Vector of as." It's ungrammatical in English just the same as it's an error in Haskell.

It's like saying "give me some butter and a has a sharp edge" instead of "give me some butter and a knife". The type signature that worked is like saying "find something you can spread with (call it k), and give me some butter and k."

Facts aren't types

You also can't use Num a instead of a like Vector a -> Num a -> Vector a because you can't put an assertion where you need a noun: "give me your key ring and a keys are made of metal so I can give you a new key ring".

Upvotes: 7

Dmytro Sirenko
Dmytro Sirenko

Reputation: 5083

Num is not a concrete type that can be used there. It is a unary type constructor that must be specialized with another type to be used. Speaking in OOP terms, Num is not a OOP-like abstract "superclass" type to be derived from, it's rather a factory that must be specialized for its usage.

So, trying to pass Num to a place that requires kind ("type" of a type) * is like trying to pass a function (:: a -> b) where a value (:: c) is required (the difference is that the former is done at type level):

ghci> :kind Int
Int :: *

ghci> :kind Num
Num :: * -> Constraint

so we can see that application of Num to any type of kind * at time of type-check produces kind Constraint and Constraint may only be used before => in type signature (to limit what types are correct in position of a in Vector a -> a -> Vector a, that's the meaning of Constraint).

Upvotes: 2

Related Questions