Breakelse: When Compiler Developers Get Bored
Breakelse seems completely nihilistic to me. Here&#x27;s an example:<p><pre><code>    if (1 == 1) {
        if (random_global == 4) {
          breakelse
        }
    }
    else {
        ...
    }
</code></pre>
I can imagine cases where inside of an if-block you want to jump into the other block, but those are cases where you need to restructure your control flow and state so that your conditionals are coherent. Providing breakelse as a trapdoor to escape is semantically catastrophic to the concept of if&#x2F;else. You can never again scan for if conditionals and rule them out; you always have to understand the entire block (and probably as a result the state of the entire program) before you can rule it out.<p>---<p>Overall though, I think these are issues of semantics rather than issues of syntax. It&#x27;s kind of an ongoing concern to figure out how to use railway oriented programming in an imperative language. You have two problems. First, you have to pick a side of the semipredicate problem:<p>- do you add complexity to your backend to do tuple returns that are mostly fast, knowing that people will largely use them after they&#x27;re not fast (i.e. too large to fit in a register)<p>- do you just use -1&#x2F;NULL and learn to love the semantic graveyard you now live in<p>- do you use output parameters and make your language look 50 years old<p>- do you add complex syntax sugar to make tuple returns act like output parameters<p>Second, you either have to settle on an existing subset of goto, figure out a new subset of goto, or... not be imperative. Existing subsets include &quot;if&quot;, &quot;match&quot;&#x2F;&quot;switch&quot;, and exceptions. Non-imperative solutions include do-notation, &quot;everything is an expression&quot;, or callbacks. These aren&#x27;t completely orthogonal; for example Python will do what you want with the new walrus operator:<p><pre><code>    def op(val):
        if callable(val):  # Sticking with the somewhat odd example here
            return &#x27;first&#x27;
        if val == &#x27;first&#x27;:
            return False
        if val == &#x27;second&#x27;:
            return &#x27;third&#x27;
    
    if (var := op) and (var2 := op(var)) and (var3 := op(var2)):
        print(var, var2, var3)
    else:
        print(&#x27;something failed, but what?&#x27;)
</code></pre>
The problem though, as you can see at the end, is it&#x27;s not super clear where in your pipeline things exploded. You can of course imagine solutions to this, but (as always) we&#x27;re verging towards full monads.<p>---<p>To my mind, I think you probably can&#x27;t do better than the Result pattern in Rust (as long as you&#x27;re willing to have a parameterizable type system). The &quot;?&quot; operator lets you cram everything on a single line, but even without it (I don&#x27;t really like &quot;?&quot; as an operator) your &quot;error handling&quot; generally avoids the pyramid of doom. Go is similar; though people hate the &quot;if val, err&quot; pattern it&#x27;s essentially the same; they&#x27;re just objecting to syntax or pining for exceptions to bail them out of error handling at all.<p>There is something very ergonomically satisfying about getting something and testing it in a single conditional. Once you get used to it it&#x27;s hard to give it up. But I don&#x27;t really think it&#x27;s a good pattern for larger state handling or error handling. Other languages split the difference with if-let or match (or both), which feels a little more &quot;right tool for the job&quot; to me.