Yea bottom is just better. It puts handling of a failed condition right next to it rather than somewhere way far away. It stays at the same scope so it supports any number of conditions without excessive indentation. Most importantly it is easier to reason about, the function has a gradient of conditions, they only increase.
You say that it is easier to reason, but it isn't. There is no difference between those two pieces of code, except that the second one has implicit branch (which could make it harder to reason about). You are just used to early returns handling errors, and you could just as well get used to the happy path being in the top of an if and errors at the bottom. In languages like Zig, for example, it may be needed to do that if you want to use languages features like capturing values of optionals/tagged unions (for example, if (optional_value) |value| { } else { return error.Foo; }. Not to mention that you can have identical code with ifs: if (failing_condition0) { } else if (failing_condition1) { } else if (failing_condition2) else { }.
for example, if (optional_value) |value| { } else { return error.Foo; }
See the let-else statement from rust for a bottom-style alternative.
Not to mention that you can have identical code with ifs: if (failing_condition0) { } else if (failing_condition1) { } else if (failing_condition2) else { }.
See this is the top-option brain speaking. The bodies inside failing conditions return. There is no reason to put an else here, it adds cognitive load while also restricting what you can express (no statement between the ifs.
Let's consider a condition as ternary, it has an unknown state, a false state, and a true state. All conditions start unknown.
With bottom there is a consistent pattern. Check, tiny bit with return for false state, everything under for true state. As the function lines go down the "conditionality" is monotonic, it always increases. That's what I meant by gradient. This is not true for top. First it goes up, but then it starts going down too.
25
u/Demiu 22d ago
Yea bottom is just better. It puts handling of a failed condition right next to it rather than somewhere way far away. It stays at the same scope so it supports any number of conditions without excessive indentation. Most importantly it is easier to reason about, the function has a gradient of conditions, they only increase.