@kainino @H2CO3
I understand your concerns, and this is not a darling of mine, but it is interesting and worth considering imo.
The foremost argument is that we already have break
-with-value from labelled blocks so this is actually no more than an extension/elaboration of the same space of ideas. We have an opportunity to reuse the syntactic and semantic commonalities of labels (e.g. 'a
) and blocks with values to create a cohesive set of tightly integrated features that work well together - rather than a completely new construct (the try
block) with its entirety own semantics.
The explicit variant proposed here is of special interest. It is an absolutely non-breaking design (if we want to be able to capture failures within a fn
) - no other proposition has this. It also does not make use of labels if the error value should exit the function.
Okay, so given that we now can make a small addition to the language that fits well with what we already have (break
, continue
, blocks and loop
s with values) do we actually want a way to capture errors in a fn
?
I do think there was a decision by the core team to go in this direction!
I agree that often the right thing to do is to break up a function, but sometimes you do want to be able to raise an error and capture it in the same function. Especially if the error is closely linked to its solution. Having the ability to short-circuit a subset of the work can make the code cleaner and easier to understand. If you then can name the exact block / level to escape from, by naming its label, then that may bring the same clarity as with break
-with-value. The ?
operator would, in fact, just be a conditional break
-with-value.
Error capturing also helps you translate some set of errors into another set of errors. You capture the errors of your callees and match
them before letting them propagate further up the call tree.
At a higher level of analysis, why use a feature like this? I think that one answer is that it helps you to maintain certain invariants. You can call any number of functions that may fail, yet you promise not to fail yourself. So you do all the fallible stuff confined within this sort of block.
In exception safe C++ there is the idea and common pattern of the critical line of a function. Above the line anything may happen, exceptions may arise. Below the line nothing can fail. Whatever you do above the line can be rolled back, in case of a failure. Keeping these lines of separation really helps a lot in writing and maintaining robust code. You never end up with a partially correct state.