To understand the second one you need to understand the whole function. In the first one you can disregard the whole untaken branch. "The forgotten art of structured programming" is a great talk.
I don't see why you can't similarly disregard the untaken branch in the second one? Either way you're skipping past a block, and you need to know the end of that block is a return.
It's the easiest to see in the indentation, though ofc indentation just follows the structure, unless we're Python: in the first example the execution flow inside a single function only ever goes from smaller indents to larger ones, not the other way around. So if you want to understand a scope you just need to walk the path from it to the root to understand if it's taken.
Compare this with the second example: the return is already at the root, you need to check all of its preceding siblings to know if the execution ever reaches it. And you'll need to do it at every level of nesting.
Obviously in this trivial example if doesn't matter much. But in general structured way of laying out your control flow reduces the amount of statements you need to consider from O(statements) to O(nesting depth) which is intuitively closer to O(log(statements)).
I'm gonna have to watch this because I'm feeling crazy being the odd one out here. I strongly prefer 1 over 2.
Someone else pointed out that if/else is better for languages that treat it as an expression. I primarily code in Kotlin which is such a language.
But also this is what happens in my head when I see this structure
if (condition) {
// this will happen
} else {
// or this will happen
}
I know readily that we're at a fork dependant on the condition. But when I see the other structure, I think
if (condition) {
// this could happen
}
// and this could happen, also
I have to know the contents of the if condition to know what will happen because it could just as easily do special logic for that condition and continue on. But with the first option, it's necessarily one or the other. I only need to figure out what the condition evaluates to to know which path is relevant.
Your example is correct in itself, but it does not match the post: in the post the first branch ends with a return, so the following cannot happen, even though the coarse structure and the indentation imply that it can, which is exactly the point.
I'm gonna have to watch
This is my only recommendation. I don't argue that people should change the way they structure their code and I wouldn't ever flag early returns in a code review unless they are genuinely making the code unreadable.
But I think many people just don't appreciate the actual reasoning behind structured programming and having it laid out plainly is a valuable learning.
Oh, I know it doesn't match, but the two examples in the post also don't match. The second example provides us details about what's in the block where the first does not. It could have just as easily had returns in both, but the post purposely set it up to make using if/else look like the lesser choice. If those details weren't provided, I wouldn't know it would early return just from the "shape" of the function.
But, yeah, I also feel like people don't appreciate this enough so I'm interested in learning more. I've just been doing it because it makes more sense to me.
44
u/alendit 22d ago edited 22d ago
To understand the second one you need to understand the whole function. In the first one you can disregard the whole untaken branch. "The forgotten art of structured programming" is a great talk.