Don't mess up the punch line
F# functions that combine other functions tend to get complex. What you should do is to extract pieces into local functions so that the end result is easier to grasp. In functional code, there’s usually the rule of downwards dependencies. Only the lower parts have access to the upper parts, not the way around. This practice is applied both on code level as on file level. This makes the application fairly easy to understand if the code is correctly structured in each file.
An example of an unorganized and fixed piece of code:
See how the second piece moves all the operator functionality to local functions, so that only the terms are passed along. There is a reason why there is a blank line between the local functions and the final result of the main function. This last line or ‘punch line’ is the most important part of every F# function, and the place experienced functional programmers will look for. Keep your last line clean or: don’t mess up your punch line.
A folded ladder is always easier to carry
Functional programs have many compositions by nature. Functions are linked together to create bigger functions. Unlike object-oriented languages that usually only link together two or three LINQ functions, the composition can contain many pieces. The way these pieces are linked together is very important, as it is the biggest selling point of functional programming: composition. We should take great care of function composition and try to write the purpose as descriptively as possible.
The following examples show two different functions that alter a sequence.
It’s far easier to read and understand the second example, as your eyes have less distance to cross. My personal rule is that only two/three functions can be linked together on a single line. After that, the folded approach is easier to read or: the folded ladder is easier to carry.
I don't like Swiss cheese
In object-oriented languages, if/else triangles of too many nested structures are considered a code smell. This smell can be found quickly due to the large gap of empty space it leaves behind.
Functional languages don’t have many if/else structures, as the code conditions often happen in pattern matching and filter functions. Still, without good code care, you could end up with the same code smell.
The below function shows that with higher-order functions, you could very quickly end up with a very unstable structure full of holes that seems to collapse at any moment.
A better way is to condense your code. Imagine implementing a left format on a text paragraph. The below example shows the same structure but with regular text, to give you an idea. This is often called the newspaper or novel metaphor.
And now condensed:
The same can be applied to the code:
💡 This example only shows formatting, not refactoring. In this last example, we should consider formatting so that the pipeline is clear and not scattered between function code.
Also note that in the first example we added explicit blank space to the code to make it more readable, while here we remove it for the same reason. It proves that code formatting is not science but preference – or art.
Conclusion
To end this controversial post, I’ll like to stress out that code formatting doesn’t have objective arguments. There’s nothing that should stop you from doing everything in a different way than described here. Code formatting is only important for its readability. Not familiarity, but readability. If your code is readable to most people, than you’ve probably done a good job.
Thanks for reading!
Stijn
Subscribe to our RSS feed