On Thursday, 11 June 2015 at 21:57:36 UTC, Dave wrote:
In regards to being faster, I'm not a big fan of exceptions in
the first place. This probably explains my perspective on them,
but I am familiar with their typical use case. And it's to
communicate errors. I'd much prefer something like what Andrei
presented during one of his C++ talks (Expected<T>). If I were to
design a language today, I might try to incorporate that somehow
with some semantic sugar and a "handle by default" mentality.

I've reordered your post a bit so I can refer to this part first. I think you and I refer to different things when we talk about returned errors, and with the definition I think you have in mind I do see how my arguments can look like the mumbling of a madman. So I'm going to clear it out first.

At a previous post here(http://forum.dlang.org/post/riiuqazmqfyftppmx...@forum.dlang.org), I've said that "I'm not talking about C style return-error-code-and-write-the-actual-result-into-a-variable-passed-by-reference - functional languages like Haskell, Scala and Rust have shown that monadic sum types of result/error are both safe(...) and easy to use(...)"

What I refer by "monadic sum types of result/error" is pretty similar in concept to Expected<T>, though what I in mind is much closer to what functional languages have, which makes allows both easier handling inside expressions and a guarantee that the underlying result will only be used in a path where it was checked that there is no error or after the user explicitly said it's OK to convert it to an exception.

This is what's done in Rust(except it's converted to panics, which are harder to log and contain than exceptions), it can be done in D, and of course a new language that chooses this approach can have syntax for it. Here is an example for how it would be used in D:

    Expected!int parseInt(string txt) {
        if (/*parsing successful*/) {
            return expected(parsedValue);
        } else {
            return error();
        }
    }

    // Propogation - functional style
    Expected!string formatNextValueText(string origNumber) {
        return parseInt(origNumber).ifOK!(
            // good path
            parsedValue => "After %s comes %s".format(
                parsedValue, parsedValue + 1).expected,
            // error path
            () => error());
    }

    // Propogation - imperative style
    Expected!string formatNextValueText(string origNumber) {
        auto parsedValue = parseInt(origNumber);
        if (auto parsedValuePtr = parsedValue.getIfOK()) {
            return "After %s comes %s".format(
                *parsedValuePtr, *parsedValuePtr + 1).expected,
        } else {
            return error();
        }
    }

    // Convertion to exception
    string formatNextValueText(string origNumber) {
        // This will throw an exception if the parsing, and
        // return the parsed value if it succeeded:
        auto parsedValue = parseInt(origNumber).enforceOK();
        return "After %s comes %s".format(
            *parsedValue, *parsedValue + 1);
    }


Now to answer the rest of your post, which actually came first:

He is saying that now anything that throws will not only be slow but also have the same limitations as returned errors.

"nothrow by default is combining the slowness of exceptions with
the limitness of returned errors."

He literally said combine the slowness of exceptions. So I don't
know how to read that other than he said it's slow. But perhaps I am just misunderstanding his wording, so perhaps it's best I just
assume I misunderstood him.

I also literally said "with the limitness of returned errors.". That part is important part of the sentence. My point is that the exception mechanism is much less limited from the returned value mechanism, because it lets you handler the error in a higher level without modifying the middle levels to acknowledge it. The price for this is slowness.

The benefits of nothrow by default come naturally with returned errors - the users can't implicitly ignore them, and since the possible errors are encoded into the return type any tool that can display the return type can show you these errors.

With nothrow by default, you are paying the performance price of an exception mechanism, and then do extra work to add to it the limitations of returned errors, just so you can get the benefits that come naturally with returned errors. Wouldn't it be better to just use returned errors in the first place?

but also have the same limitations as returned errors

That is a legitimate concern, but I don't think it is correct.
The transitive nature would enforce that you at least handle it
at some point along the chain. Nothing would force you to handle
it right away. Although I think in most cases it's far better to
do it when the error occurs(but this is my style). But when you
don't there would be at least a flag saying "this might fail"
that you and others could see. You can ignore that all the way up the turtles, but at some point you are going to be like "I should
handle these errors".

If you have to acknowledge it anyways, then might as well just use returned errors because they are faster.

I guess you could think of nothrow as an acknowledgement. I'd
view it more like a warning to others. As I said before, I don't
think you should be *forced* to do anything. Handle it right away
and you don't have to mark your own function as 'throw'. Don't
handle it, then your function should be marked 'throw', and the
compiler can help others out and tell them to expect to have to
handle the exception at some point.

This is not very different than returned errors: you either handle the error or change the function's signature so it can pass the error to it's caller.

The big benefit of unchecked exceptions is that they require minimal effort to use - when I code and encounter a path that you can't handle, I just throw an exception(I can also use helper assert/enforce functions). I don't need to interrupt the flow and start adding `throws` all over the project, making my brain do a "context switch" that makes me forget the immediate thing I was working on, and increasing the chances of my commit conflicting with other concurrent commits(because it was touching general functions all over the place). No - I throw the exception, and leave worrying to how it will be handled to later.

The alternative is not that I leave my current task to implement something that'll handle the exception at the right place - the alternative is that I continue my current task without throwing the exception, and only deal with it later on when something crashes or doesn't work properly.

Why? Because I have a task to do, and if every time I encounter a place that *might* indicate an error I'll need to make sure that error is handled, I won't be able to focus on the actual task.

Out of the unhandled error possibilities, exceptions are easiest to fix when they do happen. Forbidding unchecked exceptions doesn't mean there are no errors - just that you don't have this amazing mechanism for tracking and debugging them.

Reply via email to