I just began learning Haskell and one of the strange things for me is the syntax for the type of a function with multiple arguments.
Consider a simple example:
(+) :: Num a => a -> a -> a
Why we need all the arrows here? Wouldn’t it make more sense to write something like Num Num Num -> Num
?
What is the reason under the hood? I searched for this question but couldn't find anything really helpful.
The first thing that is confusing things is the Num a =>
, so we'll ignore that altogether for now. Instead, lets consider Int -> Int -> Int
, which is one possible specialization of the type signature you gave.
Functions are almost always curried in Haskell. This means that a multi-argument function is actually function of one argument that returns a function that takes the next argument, and so on.
The ->
is right associative, so Int -> Int -> Int
is the same thing as Int -> (Int -> Int)
.
This also means that this definition
f :: Int -> Int -> Int
f x y = x + y
is the same as
f :: Int -> Int -> Int
f x = \y -> x + y
In fact, all functions in Haskell take exactly one argument. Tuples exist as well, but they are first-class citizens so they are more than just an argument list.
The Num a =>
is a bit of a different aspect of the type system. It says that the type variable a
must be an instance of the Num
type class. Common examples of types that are instances of Num
include Int
and Double
. So Num
isn't a type itself, it is a type class. Num a =>
represents a constraint on the type variable a
, it isn't another argument for the function.
The (+)
method is a member of the Num
type class, so you must constrain a
in this way in order to use (+)
. If you try to give f
the signature a -> a -> a
(with no constraint), it won't work because a
is completely unconstrained and we know nothing about what types it can be. As a result, we couldn't use (+)
on it.
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