Haskell due to the curried nature of its functions makes partial application and composition a favorable programming style. A few days ago, I solved Project Euler problem #52 with the following elegant code:

import Data.List
import Data.Function
 
haveSameDigits = (==) `on` (sort . show)
isSingleton = (== 1) . length
isPrick = isSingleton . nubBy haveSameDigits . flip map [1..6] . (*)
main = print $ find isPrick [1..]

In some cases though, composition with (.) alone does not yield very readable expressions.

Let’s take an example. Consider the following function:

countIf pred xs = length $ filter pred xs

How do you write that with (.)? Intuition might suggest the following will work:

countIf = length . filter

But it doesn’t. The reason is currying. The type signatures of filter and length are:

filter :: (a -> Bool) -> ([a] -> [a])
length :: [a] -> Int

As you cans see, result type of filter, ([a] -> [a]) is not compatible with input type of length, [a]. The types will comply when filter is partially applied on one argument. So this is how we can rewrite countIf with (.):

countIf pred = length . filter pred

That still leaves us with one point. This is not a problem in languages like Clojure, where functions are not curried.

(def count-if (comp count filter))

Let’s ask lambdabot how to get rid of that point.

[18:52] <missingfaktor> @pl countIf pred xs = length $ filter pred xs
[18:52] <lambdabot> countIf = (length .) . filter

And yay, the point is gone!

This pattern generalizes nicely for higher adicities. For example,

add3 a b c = a + b + c
(((show .) .) . add3) 3 4 5 -- gives "12"

However that isn’t very readable. We can capture these composition patterns in new combinators like shown below:

compose2 f g = (f .) . g
compose3 f g = ((f .) .) . g

With these we can rewrite as follows the expressions we saw before:

countIf = length `compose2` filter
(show `compose3` add) 3 4 5

For each adicity n, we will have to write a separate combinator, composen. It is not possible to generalize them into one combinator.