November 30, 2020
On Monday, 30 November 2020 at 11:43:01 UTC, Ola Fosheim Grøstad wrote:

> But they are also a bit too simple. Like, if accessing external resources you often want to do retries. Annoying to throw all the way out.
>
> Consider for instance if you try to fetch a file from an url, then it fails. It would have been nice to inject a recovery handler that can analyze the failure and provide a new url, sleep then retry etc.
>
> e.g. something along the lines of this sketch:
>
> fetch_url(url) {
>   retry with (url) {
>    …download attempt…
>    …throw http_fail, server_busy…
>   } catch (…){
>    …ok cleanup, nobody wanted a retry…
>   }
> }
>
>
> main(){
>   on http_fail(url){
>     url = replace_with_backup_server(url)
>     return true; // retry
>   }
>   data = fetch_url(url)
> > }

In Ruby there's the `retry` keyword for this. Although you cannot control the recovery handler from the outside, like in your example. In D terms you would put `retry` in a `catch` block and it would run the code inside the `try` block again.

--
/Jacob Carlborg
November 30, 2020
On Monday, 30 November 2020 at 12:59:32 UTC, Jacob Carlborg wrote:
> On Monday, 30 November 2020 at 11:43:01 UTC, Ola Fosheim Grøstad wrote:
>
>> [...]
>
> In Ruby there's the `retry` keyword for this. Although you cannot control the recovery handler from the outside, like in your example. In D terms you would put `retry` in a `catch` block and it would run the code inside the `try` block again.
>
> --
> /Jacob Carlborg

In java there is a nice library solution called fail safe: https://jodah.net/failsafe/

Something like that is missing in D (code.dlang.org).

Kind regards
Andre


November 30, 2020
On Monday, 30 November 2020 at 16:02:32 UTC, Andre Pany wrote:
> On Monday, 30 November 2020 at 12:59:32 UTC, Jacob Carlborg wrote:
>> In Ruby there's the `retry` keyword for this. Although you cannot control the recovery handler from the outside, like in your example. In D terms you would put `retry` in a `catch` block and it would run the code inside the `try` block again.

Interesting, I didn't know that.

> In java there is a nice library solution called fail safe: https://jodah.net/failsafe/
>
> Something like that is missing in D (code.dlang.org).

Yes, maybe we could find ideas in high level frameworks.

I guess one simple solution with the current setup would be for the caller to provide a "progress object" which is used for tracking progress and has resolvers. When it totally fails the library can throw the progress-object which is configured to indicate the error... Kinda like NSError in Objective C, but more active.

But I think it is more forwardlooking to inject depencies based on call-tree analysis (interprocedural analysis). Then you can get static errors for wrong configuration at compile time.

Anyway, interesting topic. No clear right or wrong. The main problem is to get people to agree on protocols, which is why a language feature has some merits.






November 30, 2020
On Monday, 30 November 2020 at 09:58:36 UTC, Gregor Mückl wrote:

> What kind of error conditions are you talking about that you consider handleable locally? Do you have concrete examples? I am asking because this is way outside the experiences I have made regarding error handling and I would like to understand your perspective.


Sure, let me give a couple of examples.

Imagine you are writing a step of a pipeline that needs to support backpressure.
When you push data downstream and the call fails because downstream is overloaded, a good recovery would be to start buffering data and propagate the failure upstream when the local buffer fills up.
If we terminate the whole pipeline each time we have a minor congestion, we'll never have anything done.


Another example: I was once implementing a distributed task execution service using Zookeeper.  It had scheduler processes and worker processes distributed across multiple DCs. Only one scheduler must be active at any time, and other scheduler instances wait in stand-by mode in case the leader dies or becomes partitioned.
First, Zookeeper API is callback-based, and throwing exceptions into the event-loop not controlled by your application makes little sense.  So we already need a different error handling mechanism for such cases.
Let's see then what happens if a node looses network connection for a short period of time.
For a worker it's not a problem at all: it should continue whenever it has been doing and wait for the network to appear again.  Tasks were mainly quite expensive to run, so aborting them was a bad idea.
A scheduler cannot operate without the network, so simply crashing would be an option. However, this involves an expensive recovery procedure, and short network disruptions happened very often.  The strategy I implemented was to schedule an action that retries whatever we were trying to do as soon as the network appears again (if this process is still a leader, otherwise crash).
November 30, 2020
On Mon, Nov 30, 2020 at 06:48:05PM +0000, Roman Kashitsyn via Digitalmars-d wrote:
> On Monday, 30 November 2020 at 09:58:36 UTC, Gregor Mückl wrote:
> 
> > What kind of error conditions are you talking about that you consider handleable locally? Do you have concrete examples? I am asking because this is way outside the experiences I have made regarding error handling and I would like to understand your perspective.
> 
> 
> Sure, let me give a couple of examples.
> 
> Imagine you are writing a step of a pipeline that needs to support
> backpressure.
> When you push data downstream and the call fails because downstream is
> overloaded, a good recovery would be to start buffering data and
> propagate the failure upstream when the local buffer fills up.
> If we terminate the whole pipeline each time we have a minor
> congestion, we'll never have anything done.

Why would it terminate the whole pipeline each time there's a congestion?  Catch the exception at the right point and run fallback code, or whatever it is, then continue.  That's the whole reason 'catch' exists.

Of course, aborting because of Errors is a different story. That's to do with logic problems in the code, in which case terminating may be the only way to recover properly.  To prevent terminating the whole pipeline in that case, I'd think about partitioning the application into a master thread/process and a number of workers, and the workers may abort upon error but the master simply reschedules the work to another worker.


> Another example: I was once implementing a distributed task execution
> service using Zookeeper.  It had scheduler processes and worker
> processes distributed across multiple DCs. Only one scheduler must be
> active at any time, and other scheduler instances wait in stand-by
> mode in case the leader
> dies or becomes partitioned.
> First, Zookeeper API is callback-based, and throwing exceptions into
> the event-loop not controlled by your application makes little sense.
> So we already need a different error handling mechanism for such
> cases.
[...]

So wrap the entry point into the user code with a try/catch block, and the catch block will propagate the error using whatever suitable channel of error communication is (e.g., write a message to a error-handling socket, or send a message to a controller then quit, etc.).  If you like, you can even mark it nothrow to ensure any user code will always catch the exception instead of letting it propagate into the event loop.

Just because you use exceptions, doesn't mean it has to be used throughout the entire call stack indiscriminately.


T

-- 
It's bad luck to be superstitious. -- YHL
January 05, 2021
On Sunday, 29 November 2020 at 23:20:13 UTC, Roman Kashitsyn wrote:
>Checked exceptions in Java was a failed attempt to achieve this, they brought nothing but pain and boilerplate.
>I believe sum types + pattern matching + error propagation sugar is a much nicer solution for errors that people might care to handle. Of course, Errors don't fall into this category.

They share anyway the same problem:

Deterministic error handling with finite enumerable error types.

It wasn't only the problem of penetrating error messages when dealing with non caught exception handling in Java, but also the pressure to up propagate all the newly created downstream exceptions requiring extending the throws section of all function signatures depending on it.

And Rust didn't improve anything regarding that, but no one would ever criticize that because its Rust.

>On the other hand, most of the time the caller has even less clue on how to deal with the error: the further you go from the point where error happened, the less likely it's going to be handled in a meaningful way.

Yep, that happens but is generally bad practice. For this kind of problem chained exception are useful, because each caller is adding his context onto the error object, the last caller is then able providing a more meaningful error message based on the messages and types of errors before.
There is even no need to throw the nested exception though I like a more than few in this regard.
Maybe an option to print only the outermost exception per default?
January 05, 2021
On Monday, 30 November 2020 at 11:02:29 UTC, Ali Çehreli wrote:
>That's all... Exceptions are the simplest error management.

+1.

Please leave exceptions as default and optionally introduce other ways for specific kind of work.

>For me, their only problem is the performance impact, which I haven't even measured. :)

But exactly this is the way exceptions are defined, they should be exceptions, i.e. they should rarely pop up. With this in mind, code execution with exceptions is as fast as with no error handling at all, which isn't the case with Rust's error handling.

What you may want instead are signals which implicitly pass a pointer of possible continuations down the stack or maybe by just overwriting a global continuation variable to quickly jump in. They are interesting for cases where you expect an event to happen more than not.

Disadvantages are however:

- a small footprint registering landing pads (catch regions/continuations)
- don't fit that well with reference counted objects

January 05, 2021
On Tuesday, 5 January 2021 at 17:59:41 UTC, sighoya wrote:
> On Monday, 30 November 2020 at 11:02:29 UTC, Ali Çehreli wrote:
>>That's all... Exceptions are the simplest error management.
>
> +1.
>
> Please leave exceptions as default and optionally introduce other ways for specific kind of work.
>
>>For me, their only problem is the performance impact, which I haven't even measured. :)
>
> But exactly this is the way exceptions are defined, they should be exceptions, i.e. they should rarely pop up. With this in mind, code execution with exceptions is as fast as with no error handling at all, which isn't the case with Rust's error handling.
>
> What you may want instead are signals which implicitly pass a pointer of possible continuations down the stack or maybe by just overwriting a global continuation variable to quickly jump in. They are interesting for cases where you expect an event to happen more than not.
>
> Disadvantages are however:
>
> - a small footprint registering landing pads (catch regions/continuations)
> - don't fit that well with reference counted objects

You'd have to seriously measure the overhead - exceptions *are* nominally zero-cost but compilers don't like optimising when they can be thrown because it massively complicates the control flow analysis. That's partly what walter was referring to in the first place.

At very least exceptions should not be the default for (say) parser errors because they aren't exceptional.
January 05, 2021
On Tuesday, 5 January 2021 at 18:18:27 UTC, Max Haughton wrote:
> You'd have to seriously measure the overhead - exceptions *are* nominally zero-cost but compilers don't like optimising when they can be thrown because it massively complicates the control flow analysis. That's partly what walter was referring to in the first place.

Okay, this is new to me, could you elaborate a bit more please?

> At very least exceptions should not be the default for (say) parser errors because they aren't exceptional.

It depends, if you aren't interested in the content of the parsed file at all in case of a parser error, then exceptions might be the right choice. You throw them once, that's it.

If you are otherwise interested to gain anyway information from the file then errors in the return type make sense as they are now part of the considered information.

A good example are IDE compilers, where a parser error line- and column range is used for red marking the code. In addition, other places of parser errors have to be red marked after the same run.


1 2 3 4
Next ›   Last »