Learn You a Haskell for Great Good! - Miran Lipovaca [27]
5
ghci> (max 4) 5
5
To understand how this works, let’s examine the type of the max function:
ghci> :t max
max :: (Ord a) => a -> a -> a
This can also be written as follows:
max :: (Ord a) => a -> (a -> a)
Whenever we have a type signature that features the arrow ->, that means it’s a function that takes whatever is on the left side of the arrow and returns a value whose type is indicated on the right side of the arrow. When we have something like a -> (a -> a), we’re dealing with a function that takes a value of type a, and it returns a function that also takes a value of type a and returns a value of type a.
So how is that beneficial to us? Simply speaking, if we call a function with too few parameters, we get back a partially applied function, which is a function that takes as many parameters as we left out. For example, when we did max 4, we got back a function that takes one parameter. Using partial application (calling functions with too few parameters, if you will) is a neat way to create functions on the fly, so we can pass them to other functions.
Take a look at this simple little function:
multThree :: Int -> Int -> Int -> Int
multThree x y z = x * y * z
What really happens when we call multThree 3 5 9, or ((multThree 3) 5) 9? First, multThree is applied to 3, because they’re separated by a space. That creates a function that takes one parameter and returns a function. Then that function is applied to 5, which creates a function that will take one parameter, multiply 3 and 5 together, and then multiply that by the parameter. That function is applied to 9, and the result is 135.
You can think of functions as tiny factories that take some materials and produce something. Using that analogy, we feed our multThree factory the number 3, but instead of producing a number, it churns out a slightly smaller factory. That factory receives the number 5 and also spits out a factory. The third factory receives the number 9, and then produces our resulting number, 135.
Remember that this function’s type can also be written as follows:
multThree :: Int -> (Int -> (Int -> Int))
The type (or type variable) before the -> is the type of the values that a function takes, and the type after it is the type of the values it returns. So our function takes a value of type Int and returns a function of type (Int -> (Int -> Int). Similarly, this function takes a value of type Int and returns a function of type Int -> Int. And finally, this function just takes a value of type Int and returns another value of type Int.
Let’s look at an example of how we can create a new function by calling a function with too few parameters:
ghci> let multTwoWithNine = multThree 9
ghci> multTwoWithNine 2 3
54
In this example, the expression multThree 9 results in a function that takes two parameters. We name that function multTwoWithNine, because multThree 9 is a function that takes two parameters. If both parameters are supplied, it will multiply the two parameters between them, and then multiply that by 9, because we got the multTwoWithNine function by applying multThree to 9.
What if we wanted to create a function that takes an Int and compares it to 100? We could do something like this:
compareWithHundred :: Int -> Ordering
compareWithHundred x = compare 100 x
As an example, let’s try calling the function with 99:
ghci> compareWithHundred 99
GT
100 is greater than 99, so the function returns GT, or greater than.
Now let’s think about what compare 100 would return: a function that takes a number and compares it with 100, which is exactly what we were trying to get in our example. In other words, the following definition and the previous one are equivalent:
compareWithHundred :: Int -> Ordering
compareWithHundred = compare 100
The type declaration stays the same, because compare 100 returns a function. compare has a type of (Ord a) => a -> (a -> Ordering). When we apply it to 100, we get a function that takes a number and returns an Ordering.
Sections
Infix functions can also be partially applied by using