FYI: Second day into Haskell
MRE (what the function does, does not matter):
foo :: Int -> Int -> Int
foo x y
| x == y = x + y
| x < y = x
| x > y = y
This is what I've gathered about the ->
operator(?) so far:
Now take a look at the following function type declaration:
foo :: Int -> Int
This declares(what is the appropriate word?) a function called foo
that takes an Int
and returns an Int
.
However, the declaration: foo :: Int -> Int -> Int
declares(?) that foo
takes 2 Int
s and returns an Int
when it is the same as saying foo :: Int -> (Int -> Int)
which is a function that takes an Int
and returns a function that takes an Int
and returns an Int
.
How does this make sense? Should it not be something on the lines of Int,Int -> Int
?
PS: The book I'm using: Thinking Functionally with Haskell by Richard Bird.
No function takes two parameters. All functions take one parameter. Indeed, we can write:
bar :: Int -> Int
bar = foo 2
So what did foo 2
do? It constructed a new function, a function that will take an Int
and return an Int
. It is essentially a "specialized" version of foo
with x = 2
.
So foo :: Int -> Int -> Int
, is like your intuition said Int -> (Int -> Int)
: it takes one parameter, an Int
, and constructs a function that will then take the "second" parameter.
You can also make an "uncurried" version of foo
:
foo' :: (Int, Int) -> Int
foo' (x, y)
| x == y = x + y
| x < y = x
| x > y = y
Now it again takes one parameter: a 2-tuple with two Int
s, but thus one parameter.
Converting between the two styles happens regularly if you want to "group" parameters together, or ungroup them. Therefore there are uncurry :: (a -> b -> c) -> (a, b) -> c
[Hackage] and curry :: ((a, b) -> c) -> a -> b -> c
[Hackage], so foo' = uncurry foo
, and foo = curry foo'
.