Reputation: 155
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
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
Reputation: 32455
Num a
isn't actually a type at allNum
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 a
s and a single a
and returns a Vector of a
s."
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 a
s in the original with a single number type.
Num
on it's own doesn't make senseNum
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 a
s and an is a number type and returns a Vector of a
s." 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."
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
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