Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [38]

By Root 573 0
it, then applies the sum function to the result of that, and finally applies negate to the previous result. So it’s equivalent to the preceding lambda.

Function Composition with Multiple Parameters


But what about functions that take several parameters? Well, if we want to use them in function composition, we usually must partially apply them so that each function takes just one parameter. Consider this expression:

sum (replicate 5 (max 6.7 8.9))

This expression can be rewritten as follows:

(sum . replicate 5) max 6.7 8.9

which is equivalent to this:

sum . replicate 5 $ max 6.7 8.9

The function replicate 5 is applied to the result of max 6.7 8.9, and then sum is applied to that result. Notice that we partially applied the replicate function to the point where it takes only one parameter, so that when the result of max 6.7 8.9 gets passed to replicate 5, the result is a list of numbers, which is then passed to sum.

If we want to rewrite an expression with a lot of parentheses using function composition, we can start by first writing out the innermost function and its parameters. Then we put a $ before it and compose all the functions that came before by writing them without their last parameter and putting dots between them. Say we have this expression:

replicate 2 (product (map (*3) (zipWith max [1,2] [4,5])))

We can write this as follows:

replicate 2 . product . map (*3) $ zipWith max [1,2] [4,5]

How did we turn the first example into the second one? Well, first we look at the function on the far right and its parameters, just before the bunch of closing parentheses. That function is zipWith max [1,2] [4,5]. We’re going to keep that as it is, so now we have this:

zipWith max [1,2] [4,5]

Then we look at which function was applied to zipWith max [1,2] [4,5] and see that it was map (*3). So we put a $ between it and what we had before:

map (*3) $ zipWith max [1,2] [4,5]

Now we start the compositions. We check which function was applied to all this, and we see that it was product, so we compose it with map (*3):

product . map (*3) $ zipWith max [1,2] [4,5]

And finally, we see that the function replicate 2 was applied to all this, and we can write the expression as follows:

replicate 2 . product . map (*3) $ zipWith max [1,2] [4,5]

If the expression ends with three parentheses, chances are that if you translate it into function composition by following this procedure, it will have two composition operators.

Point-Free Style


Another common use of function composition is defining functions in the point-free style. For example, consider a function we wrote earlier:

sum' :: (Num a) => [a] -> a

sum' xs = foldl (+) 0 xs

The xs is on the far right on both sides of the equal sign. Because of currying, we can omit the xs on both sides, since calling foldl (+) 0 creates a function that takes a list. In this way, we are writing the function in point-free style:

sum' :: (Num a) => [a] -> a

sum' = foldl (+) 0

As another example, let’s try writing the following function in point-free style:

fn x = ceiling (negate (tan (cos (max 50 x))))

We can’t just get rid of the x on both right sides, since the x in the function body is surrounded by parentheses. cos (max 50) wouldn’t make sense—you can’t get the cosine of a function. What we can do is express fn as a composition of functions, like this:

fn = ceiling . negate . tan . cos . max 50

Excellent! Many times, a point-free style is more readable and concise, because it makes you think about functions and what kinds of functions composing them results in, instead of thinking about data and how it’s shuffled around. You can take simple functions and use composition as glue to form more complex functions.

However, if a function is too complex, writing it in point-free style can actually be less readable. For this reason, making long chains of function composition is discouraged. The preferred style is to use let bindings to give labels to intermediary results or to split the problem into subproblems that are easier for someone reading the code to understand.

Return Main Page Previous Page Next Page

®Online Book Reader