Flow Control - why no early returns
Are early returns really more trouble than they are worth?
5 min read
In my previous post I outlined the need for a range of new modern types in EK9.
In this article I'll discuss the different forms of flow control. I will also introduce a couple of contentious ideas relating to unnecessary complexity in most languages; those being break, label, continue and return.
Forms of flow control
There are broadly two categories of flow control in EK9.
- Decision flow control
- Iteration flow control
Decision flow control
This is the simple if/else and the switch construct. These are described in if/else and switch on the EK9 site. Both of these are fairly similar to most languages. The switch has no break syntax and significantly you can use switch to return a value.
Iteration flow control
Similar to most languages in the form of for loops, while loops and do while loops.
But again the most significant change is the lack of any break syntax to exit a loop early. The developer has to use a condition expression in the while do/while loop and must iterate through to the end in a 'for loop' (WHAT! you may think, but read on).
Give me a break
In short, no I won't; at least not in the forms most languages use. The main reason why return, break, label, continue or goto are not available is to reduce the possible processing paths a developer has to understand when looking at code.
Just think about it for a moment. If a function/method has multiple exit points you have to build a mental model of each of the processing paths. Many would say - hey I only do preconditions at the start and they are simple. Hmm -- take a look at a few code bases, that's not what people do. If they are all simple preconditions -- hey put them in a simple single if statement.
The original idea of structured programming was really innovative at the time. There will be many commentators that think reverting to single entry, single exit is a retrograde step. I'd agree if all EK9 was offering was this same old reheated soup from other languages.
But it's not, is going to try and attempt to ween developers away from just standard procedural/imperative code and more in the direction of a functional approach.
Now bear with me, don't huff and puff just yet. Given this a quick read first.
Pipelines or 'fluent interfaces'
Fluent Interfaces are a sort of Object Oriented attempt at functional programming (a bit harsh maybe).
Why is this at all relevant to for loops, break and lack of early returns?
It's relevant because with EK9 pipelines you have all the early returns, breaks and continues you need; but in the form of new syntax (unless you have a Unix background then it's familiar).
This is your if statement that either does or does not let data flow through the pipeline.
Head, Tail, Skip
This is your break.
The pipeline manages your state variables as they stream through the pipeline. When you adopt EK9 pipelines; you'll see that:
cat someList | filter by someCriteria | head 1 > stdout
Means you don't write for or if or break. You don't need any loop state variables, multiple nested loops and no labels to continue to.
This single construct from Functional programming is probably as significant as Polymorphism from Object Oriented programming.
There are lots of other commands in EK9 Pipelines for grouping, sorting, uniqueness, joining, mapping and collecting/reducing. Also even teeing off data from parts of the pipeline as it flows through (big Unix/Linux influence here).
The unspoken flow control is the Exception. EK9 uses these in a very limited way and they are 'unchecked' meaning you do not declare them on function or method signatures.
Implementation details leaking out
If Exceptions had to be declared (like some languages) then implementation details start to leak out (FileNotFoundException). It looks like you are accessing files in your implementation then. But what if you altered this implementation to read and write data from a remote system (what about those of Exceptions and then new networking Exceptions).
That's why EK9 only has unchecked Exceptions. In general it's only worth catching Exceptions if you can do something about the processing. A remote call to a web server could be retried a couple of times (that's reasonable). But not being able to write a file is pretty much it - what are you going to do to solve that?
By the way, Exceptions destroy the entire Pipeline processing if not caught!
The Unset nature of data
EK9 prefers to return unset data, this is much easier to deal with. In pipeline processes it just gets omitted from the flow; in a sort of Haskell MONAD type manner.
EK9 has traditional flow control, but it has been deliberately limited to enforce a more structured approach originally created by Böhm, Jacopini and Dijkstra.
You may see this as a limitation, but I've found it forces more decomposition in to smaller functions and methods and the overall processing is much easier to understand at first glance.
The functional approach to collection processing again encourages smaller well rounded pure functions and a more declarative syntax. Basically you don't get bogged down in the detail.
The next blog post will discuss the thorny issue of immutability and take another key innovation from the Functional programming camp (pure). In EK9 it won't be totally pure. I'm a pragmatic person and also like to bend the rules, so it will be pure-ish.