Design note: pipelines over loops
This note goes with Streams and Pipelines in EK9. EK9 supports both imperative loops and stream pipelines. It steers you to pipelines of small, named functions, even though the loop is often shorter.
The loop is shorter
There is little doubt that the same functionality can be written with much less code in an imperative style. The library example's imperative version is shorter, even before it sorts the author names. For one-off, short, simple processing it may be the better approach. And the functional approach takes more thought to design and implement than a loop with state variables.
But it does not last
- Requirements grow. With a single requirement, such as the latest book by one author, you are tempted by a loop. But depending on how long the software lives, more queries almost always follow. Adopt the pipeline early and you may write slightly more code, but it is obvious how to extend it, and you reuse what you have written.
- Loops do not reuse. Loop variables and intermediate collections built up during processing are nested in the loops and their conditions, so none of it tends to be reusable. A library of short pure functions, with records, traits and classes, can be reused and tested in isolation.
- The pattern is known. A pipeline is a standard design pattern: what it does is clear from the page. Nested, arbitrary loops take more time to 'grok', and each time similar functionality is needed it is written in a slightly different way, which leads to confusion and defects. A pipeline is a known pattern rather than some form of while, do-while or iterator loop.
- Business logic gets a name. A predicate such as 'sufficient number of books' could be an 'if statement' or a 'lambda'. As a function called 'sufficientBooks' it is explicit, and the next pipeline can use it.
- The type is known at every stage. With chained methods in other languages it can be hard to know the type at a given point. EK9's strong, explicit typing of functions and methods means the type at any stage can always be read from the source and the signatures of the stage functions.
Fewer methods on classes
The functional approach reduces the number of methods needed on classes. Small, distinct functions and classes combine in components, compositions and pipelines, rather than many capabilities living inside classes. In general, the smaller, more rounded and better documented the components, the more reliable, reusable and understandable the code is over the longer term.
A choice
EK9 does not remove the imperative style. The minute you need to make a few alterations here and there, though, the pipeline is much easier to alter and change.