January 14, 2015
On Wed, Jan 14, 2015 at 10:56:39AM -0800, Jeremy Powers via Digitalmars-d wrote:
> On Wed, Jan 14, 2015 at 3:16 AM, Paulo Pinto via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
> 
> > On Wednesday, 14 January 2015 at 10:37:31 UTC, Atila Neves wrote:
> >
> >> That post, to me, only reinforces how much better using exceptions is.
> >>
> >> Atila
> >>
> >>  They posted recently a blog post about error handling in Go.
> >>>
> >>> https://blog.golang.org/errors-are-values
> >>>
> >>> --
> >>> Paulo
> >>>
> >>
> > Agree, on the other hand is another example of the mindset.
> >
> 
> It is interesting to me that as people come up with ways to make error-code handling better it starts looking very much like exceptions.

+1.


T

-- 
Meat: euphemism for dead animal. -- Flora
January 15, 2015
On Wednesday, 14 January 2015 at 12:15:03 UTC, Ola Fosheim Grøstad wrote:
> On Wednesday, 14 January 2015 at 11:20:49 UTC, Marc Schütz wrote:
>> Without addressing anything else: You want moving here, not copying.
>
> How can you move a value, it has no identity?

When you say "copy", I expect that to mean "do a bitwise copy and call the postblit". Postblit is obviously not desired here, but merely doing the bitwise copy is enough. That's a move, because afterwards the original location isn't usable anymore (the originating thread is dead).
January 15, 2015
On Wednesday, 14 January 2015 at 17:45:28 UTC, deadalnix wrote:
> On Wednesday, 14 January 2015 at 11:17:52 UTC, Marc Schütz wrote:
>> Your claims:
>>
>> - Exceptions are for "I have no idea what to do about this" situations.
>> - "exceptions for control flow [...] makes for very unreadable/unmaintainable code"
>>
>> Ola's answer directly addresses these claims and provides a counter-example (in the last paragraph), which I happen to agree with.
>>
>> => Not a strawman.
>
> Being precise is important.
>
> The example presented (ie throwing a exception signaling a http code) is a good one and never contradict what I said.
>
> It is an example "I have no idea what to do about this". The code throwing the exception is faced with a situation where it cannot continue (assuming this code is expected to generate a webpage or something like that) but at the same time, is not in a position to perform the custom http so it is bailing out. It is signaling to higher level "I would have liked to return this http code, but have no idea how to do so and so I'm giving up."
>
> Now I see how you can consider this as a control flow, but it is vastly different from usual control flow (loops, branches, calls, ...). It is vastly different. You have no idea where you send your program into. In fact, you may not even be in in the framework that can make sens of this exception, you have no idea. Conversely, the framwork that is catching this exception have no idea where it came from, and it do not care either. It simply know that the page failed to render and that instead it should return a specific error code.

Indeed I was assuming that the code handling the HTTP request ("controller" in MVC terms) is aware of the framework, because I don't think it's common to have exchangeable server frameworks for any given web application. Given that, the use of exceptions becomes part of the framework's API. Furthermore, the code also has a very clear idea of how to handle the situation, namely instructing the framework to return a specific error code. That is not giving up, in my eyes, but actually the opposite, to answer a specific request (e.g. "GET /non-existant-file.txt") with the response prescribed by the protocal (e.g. "404 Not found"). It could just as well have called a helper `respondWith404()` provided by the framework to set a flag somewhere and return to the framework in order to it act accordingly. It's just that the mechanism of using an exception seems more convenient (but of course, de gustibus...).

Now, for a more generic library, you are right that using exceptions in this way is not a good idea. The crucial difference is IMO that in an MVC framework the application code is the one down the stack, while in many other applications it is further up the stack. Therefore, the use of exceptions (which always propagate upwards) have to be assessed differently.
January 15, 2015
On Thursday, 15 January 2015 at 19:19:22 UTC, Marc Schütz wrote:
> When you say "copy", I expect that to mean "do a bitwise copy and call the postblit". Postblit is obviously not desired here, but merely doing the bitwise copy is enough. That's a move, because afterwards the original location isn't usable anymore (the originating thread is dead).

Ah, yes, I was not thinking current D compiler internals, just "it is implementable". :)

But I think cheap/efficient exceptions are important since API consistency makes it easier to write correct code.

Single inheritance makes it difficult to use exceptions with multiple libraries since they want their own root exception. A solid ontology that outlines the most common qualities you want to filter would help a lot. Maybe also a classification mechanism that allows you to group exceptions from multiple libraries.

E.g.:
classify library1.EntityNotFound, library2.LookupFailure as NotFound;



January 15, 2015
On Monday, 12 January 2015 at 20:53:04 UTC, Walter Bright wrote:
> On 1/12/2015 10:06 AM, Tobias Müller wrote:
> On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d wrote:
>>> Yeah, exceptions are supposed to be ... well, *exceptions*, rather than
>>> the norm. :-) If you're using exceptions to do flow control, you're
>>> doing something wrong.
>>
>> But what's exceptional for you is normal for me.
>
> It's not a subjective truth, it's an objective fact.
>
> If code is throwing exceptions as part of its normal operation, it is designed wrong.

But what this means that exceptions should only ever be used for catching programming errors, some kind of "soft" assertions.
It also means, that exceptions should only be thrown, but never be actually handled, because by handling it you would "admit" that it's actually normal operation.
This is not what I call error *handling*.
January 15, 2015
On Thursday, 15 January 2015 at 19:37:33 UTC, Marc Schütz wrote:
> Now, for a more generic library, you are right that using exceptions in this way is not a good idea. The crucial difference is IMO that in an MVC framework the application code is the one down the stack, while in many other applications it is further up the stack. Therefore, the use of exceptions (which always propagate upwards) have to be assessed differently.

FWIW, although the framework I use provides exceptions for returning HTTP status, I don't use them. I have my own super-class request handler that captures everything so that I can "reformulate" caught exceptions into something that fits the REST api I expose, which have to fit what browser support (like IE9)...

The only problem I see is if the "exception type system" lacks an ontology that the framework can rely upon.E.g. that a function pass the exception on, but mistakenly executes an action because it makes a classification mistake. But that is more a problem with having an inadequate/unspecified/fuzzy exception classification mechanisms...

The language could define an ontology that allows you to express "pass through unknown", "not a failure", "no rollback" or similar as part of the exception type. Finding the exact right semantics is perhaps tricky, but also an area where there is room for practical innovation.
January 15, 2015
On Thu, Jan 15, 2015 at 09:01:35PM +0000, via Digitalmars-d wrote:
> On Monday, 12 January 2015 at 20:53:04 UTC, Walter Bright wrote:
> >On 1/12/2015 10:06 AM, Tobias Müller wrote:
> >On Mon, Jan 12, 2015 at 05:22:26PM +0000, Adam D. Ruppe via Digitalmars-d
> >wrote:
> >>>Yeah, exceptions are supposed to be ... well, *exceptions*, rather than the norm. :-) If you're using exceptions to do flow control, you're doing something wrong.
> >>
> >>But what's exceptional for you is normal for me.
> >
> >It's not a subjective truth, it's an objective fact.
> >
> >If code is throwing exceptions as part of its normal operation, it is designed wrong.
> 
> But what this means that exceptions should only ever be used for catching programming errors, some kind of "soft" assertions.

No, that's wrong. Programming errors are caught by assertions, not exceptions.

Exceptions are used for handling unexpected *environmental* circumstances, such as a file that should be there but isn't, a disk that should be able to store data but happens to be full, a network server you need to talk to that ought to be present but is down for some reason, data received from a helper program that ought to have a certain format but for whatever reason sent malformed output instead.


> It also means, that exceptions should only be thrown, but never be
> actually handled, because by handling it you would "admit" that it's
> actually normal operation.
> This is not what I call error *handling*.

Huh?! Handling unexpected circumstances is hardly "normal operations", it's making the program deal gracefully with problem situations in the environment that normally shouldn't occur. If the default server is down, switch to a backup server. If a required file is missing, fall back to a backup copy, or give up, file a log message, and clean up any resources properly before exiting. Etc..

That doesn't change the fact that problem situations should not be the norm -- if it is, then you're doing something wrong. A server shouldn't be constantly down. A required file shouldn't be mysteriously missing every other hour. Etc..

Unless, of course, the *purpose* of the program is specifically to deal with problem situations -- in which case, you wouldn't be using exceptions to indicate those situations, you'd treat them as "normal" input instead. You wouldn't be using try/throw/catch, but normal flow-control constructs.


T

-- 
It won't be covered in the book. The source code has to be useful for something, after all. -- Larry Wall
January 15, 2015
On Monday, 12 January 2015 at 00:51:25 UTC, Walter Bright wrote:
> Perhaps I misunderstand, but given A calls B calls C,
>
>    A => B => C
>
> and C detects an error, and A knows what to do with the error. B then becomes burdened with checking for the error, invoking some sort of cleanup code, and then propagating it.

There's another reason why this is not that bad (under the assumption that errors are _not_ only used for premature termination):
The meaning of an error ist in most cases only clear in combination with the function call. With every step in the call stack you lose information about the error and the handling becomes more difficult.

In a well-designed interface, every function specifies all the errors that might happen. And by this I mean a meaningful set of errors that is useful for the caller, not just the union of all errors resulting from the implementation. Simply propagating errors is not really an option in that case, because higher-level functions often also (should) have higher-level errors (the lower-level root-error should still be available though).

Some random example:
Some function needs some configuration from a file, but has no permission -> signals a permission error.
But it doesn't make sense for C to just propagate a permission error. Instead it should signal a configuration error and store the permission error as the cause of that configuration error.
January 15, 2015
On Thursday, 15 January 2015 at 21:28:59 UTC, H. S. Teoh via Digitalmars-d wrote:
> Unless, of course, the *purpose* of the program is specifically to deal
> with problem situations -- in which case, you wouldn't be using
> exceptions to indicate those situations, you'd treat them as "normal"
> input instead. You wouldn't be using try/throw/catch, but normal
> flow-control constructs.

But for almost every environmental error, there's a use case where it is normal or at least expected. That means, you have to have two versions of every function, one that throws and one that uses "normal" flow control.

Example:
Creating a file: Throws if already exists.
Creating a unique filename:
Create file.1.exp
Create file.2.exp
Create file.3.exp
... until it succeeds.
Here failure is expected and normal, still trial/error is the only option because a querying the file first is not safe.

How can I (as the implementer of the "Create file" function) decide if a failure is actually expected or not?
I can't, because I cannot foresee every use case of the function.

*Especially* with environmental errors that caller has to decide what's an error and what not.
You cannot just require certain environmental preconditions, because they can change unexpectedly.
January 15, 2015
On Thursday, 15 January 2015 at 21:48:25 UTC, Tobias M wrote:
> But for almost every environmental error, there's a use case where it is normal or at least expected. That means, you have to have two versions of every function, one that throws and one that uses "normal" flow control.

Exactly. Just think about validation of user input. Is it a service or is it a test of environmental failure?

Oh, it is a service when you validate the input with a regexp, but if the database does the validation against the db schema then it is an environmental failure. Since it is normal for users to make mistakes we now have to do double work to satisfy the no-exceptions-regime.

Or take a XML validation service. Is failure to validate not part of normal operation, yes it is. So we cannot use exceptions. But if we retrieve the XML from a XML database, then failure to validate is not normal operation, so then we can use exceptions in the validator.

From a computational view the validators are doing the same work, that means the implementation should be the same too.

> How can I (as the implementer of the "Create file" function) decide if a failure is actually expected or not?
> I can't, because I cannot foresee every use case of the function.

Reminds me of people who say that you should always test if a file exists before you open it. But to do that correctly you would have to put a lock on the file system since the file could be deleted between the test and the opening... And again, we are doing double work.

Great. With this regime in place "no overhead exceptions for normal operations" creates a lot of overhead since the computer have to do double work to avoid exceptions when using libraries and frameworks.

No wonder people don't want to use exceptions in C++.