Search code examples
haskelltypesparametersoperators

How does the arrow operator in haskell make sense in functions that take more than one parameter?


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:

  1. It is right-associative
  2. It is used in function type declaration to denote the type of function parameters as well as its return type

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 Ints 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.


Solution

  • 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 Ints, 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'.