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