comment 0

The Disappearance of the return statement

I am learning Scala.

I am sad to see that, again, like OCAML, like Rust, like CoffeeScript, Scala does not like the return  statement. It is implicit as the evaluation of the latest expression in a block.

Having no return statement allows complex but still readable conditional statement such as:

Like you see, there is no type declaration for a. The Scala compiler is able to find out itself that is a should be integer.

Instead of the classic:

Either you get rid of the inference (by declaring a as an integer) or you add a potentially useless assignment ( var a = 0) on top. The worst is to put the declaration of the variable inside the if, which restrict the scope of this variable.

So, in this case, the absence of return is more than welcome. I have no problem with this syntax, it is pretty elegant. But havind no return at all inside a function, and thus leaving a statement “as it” at the end of each code path to specify the value to return to the whole function, is a bit annoying for me.

Let’s take an example:

I call it “Star-Shaped Algorithmic” . You can have nested end point like this:

To represent the code paths of this algorithm graphically, it would look like this:

star-shaped-algorithm-2

The black dot being the entry point of the function, and the white dots being the various exit points.

When I look at this diagram, I would not be able to give which is the “right” code path, or the “normal” algorithm, or where the corner cases are handled,… Because there is no “corner case”, only “algorithm”.

This is fine, and actually this is quite recommended to have an academic code. This has many advantages

  1. Compiler might happily optimize the code path, ensure the processor pipeline is full and not broken
  2. Readability is quite good
  3. Maintenance is…. arg… ok

Maintaining this code is actually quite difficult. No in the first month. If for any reason you need to add a new case in the beginning, you’ll have to increment all the code. Not very usable.

And this also can lead to nested condition nightmare:

In Python, this is a common problem for libraries, especially for short and really useful function, and I end up sometimes recommending breaking the indentation and deal with special case earlier in the algorithms. From a “star-shaped” algorithm, we now have a “Tree-shaped” function:

Tree-shaped-algorithm-2

Now we can see a clear shape: there is a straight path, which is the “nominal” algorithm, and we have these 3 conditional outputs, probably which handle corner cases. Add a new corner case handling does not change the shape of the whole method, is easy to review, and do not increment the statement bellow.

Let’s take an real-life example. I want a method that right trim all lines in a given text.

Fine. Real life usage. Crash. What about when text  is None ? Even if this case might be handle before, I want it just to return None. Don’t ask why, I am the client.

Let’s write is star-shaped:

And Tree-Shaped:

Fine again. Real life goes on, crash in production. Why? Because text  might be a dict or anything that does not have a rstrip() . Let’s convert it to str  to ensure we can handle it like a string. I don’t want to add it in the big join line for any obscure reason.

Star-Shaped:

Tree-Shaped:

Luckily, the shape does not change!

Now I want to handle the empty string case, because, again, a crash in prod.

Star-shaped:

Tree-Shaped:

Look how the line with the join never actually changed in the “Tree-Shaped” algorithm over its life?

So when to use Star-Shape and Tree-Shape functions? For small utility methods, I would go directly to Tree-Shape. Don’t mess with academic algorithmic, think “maintainancability” and evolution first. For more central functions, that might need to be refactorized, or may grow enormously, I would recommend to use a Star shape, since it makes the refactorization more easy to do.

But with all these hate against the “ return ” keyword, I wonder if this is not a lost combat…