Composition is a great way to build complexity. You take small functions and combine them to build more complex functionality. For example, suppose you have the string “$12,000” and you need to take 10% off that and represent it as a money string. You could build a single function that does exactly that, and then go about your life until you discover that your function has a bug in it, or that it can’t be reused for a similar problem, like taking 15% off the string, instead of 10%. At this point you may be tempted to alter your function to take a parameter indicating the % to subtract from the number. This adds more complexity to a function that is already doing more than it should, and it doesn’t guarantee you won’t have to change it later on again. For example, what if now you need to be able to do that for any arbitrary money string, not just “$12,000”?

The answer is* composition.* Instead of writing a single, bug-prone, function that does way more than it should, you should write smaller functions that are easier to reason about, maintain, and which are more likely to be bug free.

Assuming you are guaranteed 100% that the input will always be in the right format, meaning, a formatted number preceded with a dollar sign, no decimal point, and that uses commas to separate thousands, you could write the following functions:

stripDollarSign removeThousandsSeparator takePercentageOff formatAsMoney

The first function would take a string, and return a string but without the dollar sign. It would be trivial to write, and almost guaranteed to be bug free.

The second function takes a string, removes the commas from it, and returns an integer. (Remember we are guaranteed the right format, so we can assume the remaining string can be cast into an integer). This function is also trivial to write and almost guaranteed to be bug free.

The third function takes an integer, subtracts a percentage from it, and returns another integer.

The last function takes an integer and formats it as a money string. Now all you have to do is call the functions one after the other:

formatAsMoney(takePercentageOff(removeThousandsSeparator(stripDollarSign("$12,000"))))

But this looks rather ugly, and it is hard to read. What we need is to be able to combine those four function into a single function that does what we want. That is what composition is.

Composition comes straight from functional programming, and it is such a great idea that I wanted to bring it to the PHP world, so I wrote the following function:

<?php /** * Compose two functions. * * Returns a new function that calls $a after $b. * * @param callable $a * The function to apply last. * @param callable $b * The function to apply first. * @return callable * A function that calls $a after $b. */ function compose(callable $a, callable $b):callable { $composed = function ($param) use ($a, $b) { $partial = call_user_func($b, $param); $whole = call_user_func($a, $partial); return $whole; }; return $composed; } ?>

This function is far from perfect, but it is a starting point. I decided to only take 2 function as parameters, instead of an arbitrary number of functions because you only ever compose two functions. When you think you are composing more functions, such as in our example, you are really composing them in pairs. You first compose the first two functions, and the returning function is composed with the 3rd one and so on.

I was so happy with my compose function that I considered putting it in packagist, but I think the Functional PHP package by **lstrojny** is a better alternative since it includes its own version of the compose function. The one thing I’m not so happy about is that the version in Functional PHP doesn’t seem to be able to take function names as parameters. This may limit the functions that you can pass to it to compose.

You may have noticed we didn’t solve the issue with wanting to take a different percentage amount off of the original amount. This can be solved easily with another concept from functional programming: *partial application*. But I will leave that for another time.