In some cases, we can use an early return to make the code shorter and reduce indentation.
But in many Nim source code I've read, people seems to use result even in cases when it would be simpler to just use return.
Example 1, they would set a result and then break out of loop instead of just return. Example 2 is something like
If we use return in the if block (as suggested by the style guide in other programming languages like Go), we don't have to indent the second block.
If I were to rewrite these two examples, I would definitely go with return, but I'm creating this thread here to make sure if using return in such cases would be frown upon of fellow Nim programmers.
as suggested by the style guide in other programming languages like Go
And here is a style guide for Nim: https://nim-lang.github.io/Nim/nep1.html#introduction-coding-conventions
"Use a procedure's implicit 'result' variable whenever possible. This improves readability."
If we use return in the if block (as suggested by the style guide in other programming languages like Go), we don't have to indent the second block.
Minimizing the number of indentation levels is a ridiculous desire, assembler language typically only has 1 indentation level -- that's a problem, not a feature.
Take this example:
if not cond: return
b()
What does hold before b()? That cond is true. How do we know this? By carefully inspecting all the code that comes before it.
Now compare this to:
if cond:
b()
How do we know this? It's obvious by the structure of the program. Structured programming is still a pretty good idea.
Many coding standards, e.g., MISRA, mandate only one return point from a function. It is claimed that this leads to easier, less error prone maintenance. I'm not sold on the idea, especially in short functions, but I'm no expert in average programmer aptitude. Since I do write software to comply with coding standards in a regulated industry, I've gotten accustomed to the style, though.
Other benefits of result that I utilize include
TL;DR: The use of result in Nim is idiomatic, so if in doubt, use it.
If we use return in the if block (as suggested by the style guide in other programming languages like Go), we don't have to indent the second block.
Personally, I wouldn't say you should minimize indentations per se. Code should be easy to understand, and if it is, I don't care if it's because there's some indentation or not. :-)
Apart from the result variable in Nim, I tend to use if with an explicit else if I want to express some symmetry. On the other hand, I sometimes use if with return at the start of a function to get some special cases out of the way, so the rest of the function can have the initial indentation level of the function.
If I were to rewrite these two examples, I would definitely go with return, but I'm creating this thread here to make sure if using return in such cases would be frown upon of fellow Nim programmers.
I wouldn't frown upon it, but it looks somewhat unidiomatic for Nim. Apart from result vs. return, I like that there's a "canonical" name for the return value. In other languages I avoid the name result for the result if that value is modified further down (along the lines of "What I assign here is not the result (yet), so don't call the variable result"). In Nim, result and its semantics are quite clear and it's idiomatic to modify result during the proc, so I have no problem with doing it, too.
Many coding standards, e.g., MISRA, mandate only one return point from a function. It is claimed that this leads to easier, less error prone maintenance.
Back in the ‘80s my main language was Pascal. Most/all(?) flavors of Pascal had this restriction. I found it forced me to write really lengthy, convoluted code when error handling was required, since errors tended to require early returns, but I had to restructure that code with conditionals to just skip the rest of the function. It led to a lot of deep, difficult to read indentation. This was one of the things that drove me away from Pascal ...
Back in the ‘80s my main language was Pascal. Most/all(?) flavors of Pascal had this restriction.
They all had the goto instruction so I have no idea what you're talking about. :-)
To maybe add some more details here, when Araq says "structured programming" he is referring to things like the structured programming theorem where all you need is if-else-while and bool variables.
On the other hand, almost every practical programming language I've heard of marketing itself as "supporting structured programming" has at least one of break/continue/return/goto because almost no human seems to think a bunch of bool variables is always easier to reason about.
For this reason, these debates about control flow representation remind me a lot of debates about "functional programming" before the "pure"/"purely" qualifers became popular (as in "pure FP"). My experience is that, for good or ill, most programmers come to the term "structured programming" by example, not by that theorem/Dijkstra-like opinions.
Break/continue/return are a middle ground between fully unstructured (goto) and purely structured (only if-else-while) in almost every sense.. lexical, logical, etc. Jump targets are limited, avoiding the control flow charts looking like a tangled mess of spaghetti { or equivalently needing a very good decluttering algo :-) That tangle was the "fact on the ground" that sold "structured programming". }
So, personally, I think these conversations would resolve more quickly & helpfully if people started using "pure"/"purely" qualifiers or hyperlinks to pure ideas. :) Degradations from "purity" are also almost always "fuzzy". So, that highlights that exceptions are more judgement calls. Also "program structuring" for many non-native English speakers might seem nearly identical to "structured programming". A new qualifier might disrupt that similarity just enough to avoid a debate. :-)
Personal experience/opinion: I did some CoffeeScript programming and at first I really liked the "everything is an expression" / "functions return the last expression of their body" paradigm. But after a while I noticed that this way, I tend lose track of what my code is actually doing. Of course, Nim lets us "implicitly return the last expression" too, but now that we have strict functions maybe we should even deprecate that for regular procs.
Seems to be pretty invalid reasoning. This want to be an imperative language. In imperative languages the main structural element of the code is the order of the instructions. Branches are secondary structural elements.
If we have early exit, we are using the primary structural tool to do checks instead of secondary method. Consider the following task: prepocess1, check1, preprocess2, check2,.... preprocessN, checkN. With early access you will get a very readable, natural imperative code structure, meanwhile using branches, you will get a huge mess of code-tree where the main (and actually most important) process is deeply indented and obscured.
Human mind is much better in interpreting linear control flow than parallelism (either a runtime or a logical one).
Deep nesting is just as bad as linear programming for the following reasons:
1. When you start with deep nesting, it leads to confusion because:
a. You quickly lose track of your main point, which results in problems such as:
i. The main point being buried under layers of subpoints, leading to issues like:
1. Subpoints that themselves contain subpoints, making it difficult to follow because:
a. Each subpoint can have multiple tangents, resulting in:
i. A structure that is hard to navigate.
ii. Making it hard to follow due to factors like:
1. Constantly having to refer back to previous points, which causes:
a. Frequent loss of context.
b. And then you find yourself needing to constantly scroll, which has its own drawbacks, such as:
i. Scrolling up and down to figure out the context, leading to:
1. Interruptions in the thought process, because:
a. It breaks the flow of understanding.
ii. Just to figure out what you were talking about, which necessitates:
1. Repeatedly reviewing previous points, causing:
a. Mental fatigue and frustration.
2. Every point you make ends up feeling like a nested Russian doll for the following reasons:
a. You open one layer to reveal another, exemplified by:
i. Each point containing subpoints, which themselves contain more points, making it feel like:
1. An endless process, similar to:
a. Opening a doll only to find a smaller one inside.
ii. Only to find another layer inside, demonstrating:
1. The complexity increasing with each level, which:
a. Leads to deeper layers of confusion.
b. And you keep going, resulting in outcomes such as:
i. Forgetting why you started, because of:
1. The overwhelming number of layers, which means:
a. Losing track of the initial objective.
ii. In the first place, indicating:
1. The original point becomes obscured, due to:
a. The depth of the nested structure.
3. By the time you get to the end, the process has these effects:
a. You realize you've written something much more complex, like:
i. A dissertation, which happens because of:
1. The extensive detail required, turning:
a. A simple idea into a complicated document.
ii. Instead of a simple comment, highlighting:
1. The inefficiency of deep nesting, due to:
a. The unnecessary complexity added.
b. And everyone is too tired to engage, for reasons such as:
i. Being too tired to read it, because:
1. The structure is overly complex, making:
a. Engagement difficult.
ii. Or understand it, leading to:
1. Miscommunication, because:
a. The intended message gets lost in the layers.
Jokes aside, personally I think a balance between indentation and linear flow is important.
You can use procs and templates to avoid excessive nesting and then you can still see the join points in the control flow graph, whereas you lose them with return.
But hey, what do I know about formal reasoning...
FWIW, we've had great success eliminating pointless bugs by avoiding result in most code: https://status-im.github.io/nim-style-guide/language.result.html#practical-notes - both in nested and early-return code.
Even if you insist on using result, you can avoid many of its pitfalls and the unnecessary repetition highlighted in the original question:
result =
if a: 42
else: 54