Haskell noob here.
Im trying to make the following function and this error is extremely cryptic to me:
:{
myfunc::(Double a)=> a -> a -> a
myfunc a b = a b
:}
and I get:
error:
• Expected kind ‘* -> Constraint’, but ‘Double’ has kind ‘*’
• In the type signature: myfunc :: (Double a) => a -> a -> a
The same goes for Int.
However, it I use Num type, I get no error:
:{
myfunc::(Num a)=> a -> a -> a
myfunc a b = a b
:}
The same goes if I use just plain Int or Double without Double a
:{
myfunc::Double -> Double -> Double
myfunc a b = a b
:}
My question: How come is it, that I can use Num a, or just plan Double, Int, but I cannot use Double a, or Int a?
Regards.
CodePudding user response:
First a terminology note: when GHC writes *
here in the error messages, read it as Type
, the kind of types†. It is for historical reasons that older versions of GHC write it with the asterisk symbol.
So, your error message is
• Expected kind ‘Type -> Constraint’, but ‘Double’ has kind ‘Type’
• In the type signature: myfunc :: (Double a) => a -> a -> a
Ok, ‘Double’ has kind ‘Type’
– that should make enough sense, right? Double
is a type.
But why does it say it's “expecting” Type -> Constraint
? Well, it means you tried to use Double
as if it were of this kind. Namely, you wrote Double a
. Here, a
has to be a type, but you can't apply a type to another type, that would be like applying a character to a character on the value level:
Prelude> 'x' 'y'
<interactive>:1:1: error:
• Couldn't match expected type ‘Char -> t’ with actual type ‘Char’
• The function ‘'x'’ is applied to one argument,
but its type ‘Char’ has none
By contrast, Num
is a type class, and as such it has in fact the correct kind:
Prelude> :k Num
Num :: Type -> Constraint
Constraint
is the correct kind that's expected on the left of the =>
, so that's why you can write
myfunc :: Num a => a -> a -> a
Again, Double
is a type, not a type class, so it can't be used in this way. However, what you can use is an equality constraint:
{-# LANGUAGE TypeFamilies #-}
myfunc :: a~Double => a -> a -> a
myfunc = ( )
This is effectively the same as
myfunc :: Double -> Double -> Double
(there are some subtle differences with respect to typechecking).
Vice versa, it is not possible to write
myfunc :: Num -> Num -> Num
myfunc = ( )
error:
• Expecting one more argument to ‘Num’
Expected a type, but ‘Num’ has kind ‘Type -> Constraint’
• In the type signature: myfunc :: Num -> Num -> Num
†To be completely precise, Type
is the kind of lifted types. Don't worry about what that means: unless you're doing very low-level stuff, you may never need to work with unlifted types.