4 ms·
I found the blog post a bit hard to follow but there doesn't seem to be a need for exceptions here. This seems more like if (cond) <do stuff> if (an
by 22c 3y ago
I found the blog post a bit hard to follow but there doesn't seem to be a need for exceptions here.
This seems more like
if (cond)
<do stuff>
if (anothr_cond)
my_else()
else
<do more stuff>
end
else
my_else()
end
but with syntax sugar so that the nested ifs are tucked away (and perhaps with my_else() scoped to the parent if).
- FeepingCreature 3y agoExactly! The big idea is that usually the `else` will do a nonlocal exit, so we should try to rewrite this in the "flat/early-out style." But then if you're declaring intermediate variables, you end up with duplicate lines: auto var = op; if (!var.cond) return my_else; auto var2 = op2(var); if (!var2.another_cond) return my_else; do more stuff; There's this sort of very regular block in there, of "declare variable, test condition, nonlocal exit". So the idea was to combine the `if (!var.cond) return my_else` into one expression-level location, so it turns out as auto var = op? else return my_else; auto var2 = op2(var)? else return my_else; <do more stuff> // Or instead if (auto var = op?) { auto var2 = var.op2?; <do more stuff> } Then the question was, what's the best way to get both of those? And it occurred to me what I actually wanted was just a way to branch out of the if entirely, I didn't really care about preserving any values in the alternate case, I just wanted to tear the entire expression/scope down and do something else.
- _old_dude_ 3y agoMonad to the rescue Optional.of(op) .filter(v -> !v.cond) .map(v -> op2(v)) .filter(v2 -> !v2.cond) .map(/* do more stuff */) .orElseGet(() -> my_else)
- FeepingCreature 3y agoYep, in an expression language that's how you'd do it. However, we're an imperative language, and the advantage of `else` is that we can use an imperative construct, `if()`, to locate the else case in the code - so we don't need to handle it in the typesystem. That's an inversion from monadic languages, where types contain/emulate statements - in C-likes, statements contain expressions/types! So `breakelse` effectively is glue that lets us turn part of the type into a control flow, which is deliberately not represented in the typesystem. `Optional.case(:else: breakelse).X` and now in X, we're no longer dealing with an `Optional` at all. We're not "inside" the Optional as we would be with `map`; we've effectively everted the entire `Optional` using non-local flow from the `else` case.