Learn You a Haskell for Great Good! - Miran Lipovaca [32]
Note
This function has a type of numLongChains :: Int because length returns an Int instead of a Num a. If we wanted to return a more general Num a, we could have used fromIntegral on the resulting length.
Mapping Functions with Multiple Parameters
So far, we’ve mapped functions that take only one parameter (like map (*2) [0..]). However, we can also map functions that take multiple parameters. For example, we could do something like map (*) [0..]. In this case, the function *, which has a type of (Num a) => a -> a -> a, is applied to each number in the list.
As you’ve seen, giving only one parameter to a function that takes two parameters will cause it to return a function that takes one parameter. So if we map * to the list [0..], we will get back a list of functions that take only one parameter.
Here’s an example:
ghci> let listOfFuns = map (*) [0..]
ghci> (listOfFuns !! 4) 5
20
Getting the element with the index 4 from our list returns a function that’s equivalent to (4*). Then we just apply 5 to that function, which is the same as (4*) 5, or just 4 * 5.
Lambdas
Lambdas are anonymous functions that we use when we need a function only once.
Normally, we make a lambda with the sole purpose of passing it to a higher-order function. To declare a lambda, we write a \ (because it kind of looks like the Greek letter lambda (λ) if you squint hard enough), and then we write the function’s parameters, separated by spaces. After that comes a ->, and then the function body. We usually surround lambdas with parentheses.
In the previous section, we used a where binding in our numLongChains function to make the isLong function for the sole purpose of passing it to filter. Instead of doing that, we can also use a lambda, like this:
numLongChains :: Int
numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100]))
Lambdas are expressions, which is why we can just pass them to functions like this. The expression (\xs -> length xs > 15) returns a function that tells us whether the length of the list passed to it is greater than 15.
People who don’t understand how currying and partial application work often use lambdas where they are not necessary. For instance, the following expressions are equivalent:
ghci> map (+3) [1,6,3,2]
[4,9,6,5]
ghci> map (\x -> x + 3) [1,6,3,2]
[4,9,6,5]
Both (+3) and (\x -> x + 3) are functions that take a number and add 3 to it, so these expressions yield the same results. However, we don’t want to make a lambda in this case, because using partial application is much more readable.
Like normal functions, lambdas can take any number of parameters:
ghci> zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]
[153.0,61.5,31.0,15.75,6.6]
And like normal functions, you can pattern match in lambdas. The only difference is that you can’t define several patterns for one parameter (like making a [] and a (x:xs) pattern for the same parameter and then having values fall through).
ghci> map (\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]
[3,8,9,8,7]
Note
If a pattern match fails in a lambda, a runtime error occurs, so be careful!
Let’s look at another interesting example:
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
addThree :: Int -> Int -> Int -> Int
addThree' = \x -> \y -> \z -> x + y + z
Due to the way functions are curried by default, these two functions are equivalent. Yet the first addThree function is far more readable. The second one is little more than a gimmick to illustrate currying.
Note
Notice that in the second example, the lambdas are not surrounded with parentheses. When you write a lambda without parentheses, it assumes that everything to the right of the arrow -> belongs to it. So in this case, omitting the parentheses saves some typing. Of course, you can include the parentheses if you prefer them.
However, there are times when using the currying