On 09/03/2018 02:49 AM, Paul Backus wrote:
On Monday, 3 September 2018 at 04:49:40 UTC, Nick Sabalausky (Abscissa)
wrote:
Note that the above has *nothing* to do with retrieving a value.
Retrieving a value is merely used by the implementation as a trigger
to lazily decide whether the caller wants `foo` or `tryFoo`. Going out
of scope without making the choice could also be considered another
trigger point. In fact, this "out-of-scope without being checked"
could even be used as an additional trigger for even the non-void
variety. After all: what if an error occurs, but the caller checks
*neither* value nor hasValue?
The thing is, triggering on explicit access lets you handle errors
lazily, whereas triggering at the end of the scope forces you to handle
them eagerly.
Vladimir's `Success` type is, essentially, a way for a function to send
something back up the stack that its caller is forced to acknowledge.
Yes, that's correct.
Throwing an exception is *also* a way for a function to send something
back up the stack that its caller is forced to acknowledge.
Yes, but it's heavier-weight AND prevents the callee from being nothrow.
but when it comes to overall control-flow
semantics, they are basically equivalent.
Control-flow semantics, sure, but as I pointed out in my previous
sentence, there's more relevant things involved here than just control
flow semantics.
By contrast, a function that returns an `Expected!T` does *not* force
its caller to acknowledge it. If an error occurs, and the caller never
checks value or hasValue...nothing happens.
That's called squelching an error, and its EXACTLY the same problem as
using non-Expect return values to indicate errors. I'd regard that as
very notable hole in any Expected design as it breaks one of the core
points of using Expect vs returning plain error codes: The user can
still accidentally (or deliberately) squelch an error.
To clarify: If the caller never checks value or hasValue, that does NOT
mean the caller has carefully and deliberately chosen to disregard the
error. It *could* mean that, but it could also mean they simply messed
up. Deliberately squeching an error should NEVER be implicit, it should
always require something like:
catch(...) { /+ Do nothing +/ }
or
if(!x.hasValue) { /+ Do nothing +/ }
That's what being lazy
means: if you never open the box, it doesn't matter whether the cat is
alive or dead.
I don't see the laziness here as being the core point. The laziness is
the "how", not the raison d'etre. The laziness is simply a tool being
used to achieve the real goals of:
- Allowing the caller the decide between foo/tryFoo versions without the
API duplication.
- Decreasing exception-related overhead and increasing utility of nothrow.
Having one specialization be lazy and one be eager
would be a nightmare for anyone trying to use the library.
Vladimir's Success vs Expect!T is NOT an example of "eager vs lazy". In
BOTH cases, the callee treats errors lazily. And in BOTH cases, the
caller (or whomever the caller passes it off to) is expected to, at some
point, make a deliberate, explicit choice between "handle or throw". And
as I said before, allowing the caller to accidentally (or implicitly)
squelch the error is a fundamental breakage in the whole point behind
Except.