I am new to Haskell. I am just confused by the difference between the argument type declarations here:
someFunction :: (Integral a) => a -> Bool -> a
This will compile.
someFunction :: (Integral a, Bool b) => a -> b -> a
Yet given this, the compiler will complain:
• Expecting one fewer arguments to ‘Bool’ Expected kind ‘* -> Constraint’, but ‘Bool’ has kind ‘*’ • In the type signature: someFunction :: (Integral a, Bool b) => a -> b -> a
What is the difference between the two declarations? I tried to search with Google but it seems there is no direct answer to this one.
Thanks!
Thanks for the quick answers guys. And they lead me to the next question: what are the thoughts behind having argument constraints to be in different places in the declaration? (vs C-like languages where the constraints, abstract or concrete, are mixed together in the declaration)
Integral
is not a type: it is a typeclass. It describes a constraint on a type.
So, the signature:
someFunction :: (Integral a) => a -> Bool -> a
means that "As long as a
is an instance of Integral
, this function can work on it".
Your second example of (Integral a, Bool b) => a -> b -> a
does not make sense, because Bool
is a type, not a typeclass.
What does it mean to be an instance of Integral
? Well, it means that somewhere, someone has written, for some type, for instance Int
:
instance Integral Int where
-- (declarations here)
Here's an example of your own typeclass: Sizeable
:
class Sizeable s where
size :: s -> Int
We can see this as types which have an associated size, which is defined by the size
function. So, we can add some instances:
instance Sizeable [a] where
size = length
instance Sizeable (a,b) where
size _ = 2
instance Sizeable (a,b,c) where
size _ = 3
So, we could write the function:
sizePlusOne s = (size s) + 1
And this would have the type:
sizePlusOne :: (Sizeable s) => s -> Int
The items on the left of the =>
are type constraints. Haskell uses type classes for it. A type class is best seen as an interface in programming languages like Java, etc.
For instance in Haskell there is a class Eq
:
class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool
Now we can make a type Int
a "member" of the Num
typeclass, by declaring it an instance
of Num
, and **implement the functions that are part of the class
. Say for instance we want to make the (positive) Peano numbers an instance of Eq
, we then first define the Peano numbers as:
data Peano = Zero | Succ Peano
and now we can make it an instance of Eq
:
instance Eq Peano where
Zero == Zero = True
s(x) == s(y) = x == y
_ == _ = False
Zero /= Zero = False
s(x) /= s(y) = x /= y
_ /= _ = True
So now we can compare two Peano
instances with ==
, and Peano
is registered as an instance of Eq
. So that means that if we write something like:
someFunction :: Eq a => a -> a -> a -> Bool
someFunction x y z = x == y && y == z
then a
can be Peano
, since we require that Eq Peano
(on the left side) holds.
Type classes are thus used to allow you to use abstract functions (like for instance ==
and /=
), without knowing how these are defined for the concrete type.
Bool
on the other hand is not a typeclass, it is a concrete type. So you can not write Bool b
. It also makes no sense syntactically, since a type class requires a parameter (i.e. Eq Peano
), whereas Bool
is a type with no type parameter).
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