Class hierarchy and RoboDog
In object-oriented programming, you define your classes.
For example, you specify a parent class
Animal that has a method
move. You continue by creating classes
Dog which will inherit
Animal and add their own methods
Then you decide to develop a class
Robot which has a method
Now, what if you want to create a class
RoboDog that needs to
chargeBattery and also, do a
roboBark that enhances
bark of your
Dog? You need to inherit from both
RoboDog will look like this:
Function composition is based on the use of monadic (unary) curried and preferably pure functions.
Function composition is a quite simple use of multiple functions where each function receives input and hands over its output to the next function:
To make this even easier, you can define higher-order functions
composePipe differ in the order under which they compose functions together:
Note that we could use a point-free style of code (tacit programming):
This is possible because
compose(plusA, plusB) is a higher-order function.
compose returns a function that is used to define your new expression.
If you like Unix, you can also relate function composition to Unix pipeline which works on the same principle:
$ ls -l | grep key | less.
If you look at the image above you can see three numbered sets of different colours that are connected by functions g and f. Function g takes argument Horse and returns Horn. Function f then takes argument Horn and returns Unicorn. Composition of those two functions is then a function takes a Horse and returns directly a Unicorn.
Because we are using pure functions that always return the same value for the same input, it is proven that we could replace our composed function by a simple function that just takes Horse and returns Unicorn. That is a principle that is used in Memoization.
Functional programming doesn’t make it just possible to better optimise for parallel processing. As you can see, it also provides the magic that allows us to skip processing altogether and just return the answers to the questions by skipping everything in between.
Function composition and RoboDog
The use of function composition is actually in a spirit similar to what we did in the object-oriented programming example with
RoboDog and its dependencies. However, I believe that function composition is far more elegant.
Notice that the code above does not reference anything that it does not need which means that there is no mention of functions of
Robot. Those are not unique to our
RoboDog and we want to focus only on a new unique code.
To use all the features in your code you are freely using functions from
There is another significant difference between composition and objects. Objects hold internal data and status. They are stateful. Functions in functional programming, however, should be pure and stateless.
A pure function is driven only by its input to provide its output. It does not change (mutate) any other data, and it does not trigger any side effects. That makes it very simple, predictable, easy to test and easy to follow general programming best practices. These are things that a good programmer should care about.
In functional programming, you are intended to follow separation of concerns to decouple execution of a task from its implementation by the use of inversion of control principle and functional monads.
Even, if you don’t use monads (because of their definition: a monoid in the category of endofunctors, scares you), you can still decouple your code. Merely move the definition of functions into one place and execution, where you integrate and provide data, to another location. Ideally, do that on a level of completely different modules.
Cover your functions with unit tests and your integrations with integration tests and you will live happy-programmer life.
Break down your functions and use composition
You may be using functions as a box for a repeatable sequence of statements like this:
The above function trims the string argument, decorates it and then returns. In reality, it is not rare to see functions that are expressed by dozens of lines of code.
Single responsibility principle states that: Every function should have responsibility over a single part of the functionality. That is open to interpretation, but I can easily claim that the above function both trimming and decorating does two things instead of one.
Composing your functions means that for every step of your program logic you have a function that is testable and reusable.
Test-driven development asks you to write a test first for any part of the functionality that you are going to implement, let it fail and then implement it to allow the test to pass. That is partially to ensure that your program won’t have any hidden untested logic.
Create reusable code with partial application
The above example of
simonSays function could be further improved by a partial application of the curried function.
Partial application means that you supply argument exposing the underlying function(s) of a higher-order function:
Investigate your code
Because we have been using pure functions it is quite easy to insert additional functions in the composition. See this example of logging:
If you are creating pure functions you will always be able to compose your code very easily without refactoring the previous code to support the new use case.
Function composition asks you to think differently about the way you write your code and rewards you with many benefits.
Hierarchy replaced by composition allows you to always focus on developing new unique code by thinking of skills instead of classes.
The point-free style allows you to shorten your code by taking advantage of curried and higher-order functions.
You are driven to build broken down atomic functions to create more reusable composable code for single responsibility principle and test-driven development.
Pure functions and partially applied functions allow you to advance your composition by creating powerful code that is very simple, predictable, comfortable to test and easily applies programming best practices.