Jump to page: 1 24  
Page
Thread overview
Alternatives to exceptions for error handling
Nov 22, 2020
Roman Kashitsyn
Nov 22, 2020
Paulo Pinto
Nov 22, 2020
Paul Backus
Nov 23, 2020
IGotD-
Nov 23, 2020
Paul Backus
Nov 23, 2020
H. S. Teoh
Nov 23, 2020
Vladimir Panteleev
Nov 23, 2020
Adam D. Ruppe
Nov 23, 2020
Paul Backus
Nov 23, 2020
Paul Backus
Nov 30, 2020
Elronnd
Nov 23, 2020
Adam D. Ruppe
Nov 23, 2020
Jacob Carlborg
Nov 24, 2020
Jonathan M Davis
Nov 29, 2020
Roman Kashitsyn
Nov 29, 2020
Jacob Carlborg
Nov 29, 2020
Roman Kashitsyn
Nov 29, 2020
Adam D. Ruppe
Nov 30, 2020
IGotD-
Nov 30, 2020
Gregor Mückl
Nov 30, 2020
Ali Çehreli
Nov 30, 2020
Jacob Carlborg
Nov 30, 2020
Andre Pany
Jan 05, 2021
sighoya
Jan 05, 2021
Max Haughton
Jan 05, 2021
sighoya
Nov 30, 2020
Roman Kashitsyn
Dec 01, 2020
H. S. Teoh
Jan 05, 2021
sighoya
Nov 24, 2020
H. S. Teoh
Nov 24, 2020
IGotD-
Nov 24, 2020
H. S. Teoh
Nov 24, 2020
Adam D. Ruppe
Nov 24, 2020
H. S. Teoh
November 22, 2020
In the video from DConf 2020 about @live functions (https://youtu.be/XQHAIglE9CU) Walter mentioned that all @live functions are nothrow.  He also thinks that exceptions are obsolete.

I've been programming in Google's C++, Go & Rust for quite a few years and I couldn't agree more with this assessment.  Error handling is a very important part of program engineering, and having errors reflected in function return types is very useful in practice.  This helps a lot with constructing correct programs.

So my question is: does/will D provide support for alternative approaches to error handling?  In practice, this typically includes 2 things:

1. A sum type that contains either a result or an error. Like Result in Rust or Expected that Andrei proposed for C++.  It's easy to implement in D, but having this in Phobos would be nice.

2. Some kind of syntactic sugar for propagating errors upstream (? macro in Rust, check/handle proposal in Go https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md, an ugly macro in C++, etc.).
November 22, 2020
On Sunday, 22 November 2020 at 17:37:18 UTC, Roman Kashitsyn wrote:
> In the video from DConf 2020 about @live functions (https://youtu.be/XQHAIglE9CU) Walter mentioned that all @live functions are nothrow.  He also thinks that exceptions are obsolete.
>
> I've been programming in Google's C++, Go & Rust for quite a few years and I couldn't agree more with this assessment.  Error handling is a very important part of program engineering, and having errors reflected in function return types is very useful in practice.  This helps a lot with constructing correct programs.
>
> So my question is: does/will D provide support for alternative approaches to error handling?  In practice, this typically includes 2 things:
>
> 1. A sum type that contains either a result or an error. Like Result in Rust or Expected that Andrei proposed for C++.  It's easy to implement in D, but having this in Phobos would be nice.
>
> 2. Some kind of syntactic sugar for propagating errors upstream (? macro in Rust, check/handle proposal in Go https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md, an ugly macro in C++, etc.).

You won't need ugly macros in C++,

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf
November 22, 2020
On Sunday, 22 November 2020 at 17:37:18 UTC, Roman Kashitsyn wrote:
> So my question is: does/will D provide support for alternative approaches to error handling?  In practice, this typically includes 2 things:
>
> 1. A sum type that contains either a result or an error. Like Result in Rust or Expected that Andrei proposed for C++.  It's easy to implement in D, but having this in Phobos would be nice.

The biggest blocker for this is that D currently has no equivalent of Rust's `#[must_use]` and C++'s `[[nodiscard]]`. I have a DIP in the works to address this:

https://github.com/dlang/DIPs/pull/193

> 2. Some kind of syntactic sugar for propagating errors upstream (? macro in Rust, check/handle proposal in Go https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md, an ugly macro in C++, etc.).

So far the only proposal I've heard on this front is the idea of bringing Herb Sutter's deterministic exceptions [1] to D. But there are no concrete plans at this point, not even a DIP.

My expectation is that, like in Rust, the first thing to arrive will be monadic `map`/`flatMap` methods, since those can be implemented using only library code.

[1] http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0709r0.pdf
November 23, 2020
On Sunday, 22 November 2020 at 18:21:44 UTC, Paul Backus wrote:
>
> The biggest blocker for this is that D currently has no equivalent of Rust's `#[must_use]` and C++'s `[[nodiscard]]`. I have a DIP in the works to address this:
>
> https://github.com/dlang/DIPs/pull/193
>
>> 2. Some kind of syntactic sugar for propagating errors upstream (? macro in Rust, check/handle proposal in Go https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md, an ugly macro in C++, etc.).
>
> So far the only proposal I've heard on this front is the idea of bringing Herb Sutter's deterministic exceptions [1] to D. But there are no concrete plans at this point, not even a DIP.
>
> My expectation is that, like in Rust, the first thing to arrive will be monadic `map`/`flatMap` methods, since those can be implemented using only library code.
>
> [1] http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0709r0.pdf

Doesn't the lean exceptions what Herb Sutter proposes have the same problem as normal exception, that the data flow analysis gives up. Anything that requires unwinding is not going to fit for that purpose.

Rust error handling does not rely on exceptions or unwinding but rather just a return type which is a tagged union. Essentially Rust error handling is like good old return values. The only difference is that compiler inserts code when the result type isn't handled or passed to the caller. I don't understand why nobody thought about that previously because it is kind of simple.

Any method that removes the dependency to libunwind gets my vote.

November 23, 2020
On Monday, 23 November 2020 at 00:12:28 UTC, IGotD- wrote:
> On Sunday, 22 November 2020 at 18:21:44 UTC, Paul Backus wrote:
>>
>> [1] http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0709r0.pdf
>
> Doesn't the lean exceptions what Herb Sutter proposes have the same problem as normal exception, that the data flow analysis gives up. Anything that requires unwinding is not going to fit for that purpose.

In Herb Sutter's proposal, throwing an exception is syntax sugar for a return statement. There is no stack unwinding.
November 23, 2020
On Sunday, 22 November 2020 at 18:21:44 UTC, Paul Backus wrote:
> The biggest blocker for this is that D currently has no equivalent of Rust's `#[must_use]` and C++'s `[[nodiscard]]`. I have a DIP in the works to address this:
>
> https://github.com/dlang/DIPs/pull/193

This is great to see. Thank you for working on this.

Another approach that I'm not sure we've explored fully is to outright deprecate silently discarding all function return values. Functions which return a value that is only sometimes useful seem like a relatively rare oddity. I took a stab at implementing this as a DScanner check earlier this year:

https://github.com/dlang-community/D-Scanner/pull/819

Perhaps the most interesting product of the above is seeing what things were affected in Phobos. Most occurrences were in unit tests, which is understandable, and said breakages are probably more tolerable. Some valid patterns were affected though, such as returning `this` (as in the builder pattern). Perhaps it would make more sense to instead allow annotating functions as that it's safe to discard their return value (@discardable or such)?

November 23, 2020
On Monday, 23 November 2020 at 02:14:33 UTC, Vladimir Panteleev wrote:
> Some valid patterns were affected though, such as returning `this` (as in the builder pattern). Perhaps it would make more sense to instead allow annotating functions as that it's safe to discard their return value (@discardable or such)?

That might work. I also frequently return values on setters and that might get tricky as well.

@property int foo(int v) { return this.v = v; }

that kind of thing.

@discardable would be possible there too.

But then there's also generic forwarders:

struct Wrapped(T) {
    T t;
    auto opDispatch(string name, Args...)() { return t[name](args); }
}

you know. And if it wraps one of those builder style things, it should probably inherit the discardable.... but that's a big complication.

Odds are I'd actually just mark the whole wrapped thing @discardable anyway.

(and we can say this same thing about @nodiscard, but I think it is less important to forward that since it at least still compiles if it is not there.)


There's also some common C functions that could get annoying. memcpy, for example, unconditionally returns the destination pointer you passed in. Obviously we could just mark the binding @discardable too.


I currently lean toward @nodiscard as being easier to use but I do expect @discardable would work pretty well too, just with a more involved transition period.
November 23, 2020
On Sunday, 22 November 2020 at 17:37:18 UTC, Roman Kashitsyn wrote:
> 1. A sum type that contains either a result or an error.

I think it would also be good to have implicit construction of return value.

Sum!(Error, Result) foo() {
    return Error(0);
}

That's currently an error, you must explicitly mention the return type by name or by typeof return.

return typeof(return)(Error(0)); // works, but wordy

Of course you can alias the sum type,

alias EC!T = Sum!(Error, T);

EC!Result foo() { return EC!Result(r); }

but still.

So if the compiler would automatically insert that constructor call, when necessary, it'd probably make it a lot easier to use. You can still just return normal stuff syntax-wise.


> 2. Some kind of syntactic sugar for propagating errors upstream (? macro in Rust, check/handle proposal in Go https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md, an ugly macro in C++, etc.).

Yes, the ? thing in rust is pretty cool.

I was thinking about this earlier today too and perhaps reusing the `try` or `catch` keywords could do it as well.

But what I'd like to do is have it work with user-defined types too, not just compiler magic. So it expands into something like:

// you write
auto r = foo()?; // or auto r = try foo(); or whatever syntax

// and it becomes:
auto tmp = foo();
if(auto error = tmp.opError())
    return error;

auto r = tmp.opValue();


Where the opError and opValue are just defined names, similar to operator overloading, that any type can define.


Combined with the above implicit return construction and other existing D features, this would enable a lot of things. For example:

---
struct null_is_error {
        void* ptr;
        int opError() { ptr is null ? .errno : 0; }
        auto opValue() { return ptr; }
}

import core.stdc.stdlib;

ErrnoOr!(void*) main() {
        auto p = malloc(5).null_is_error?; // note Rust-style ?
}
---

This expands into:

---
auto tmp = malloc(5).null_is_error; // UFCS ctor call of helper btw
if(auto err = tmp.opError())
    return err; // which implicit constructs into return typeof(return)(err);
auto p = tmp.opValue();
---

So we're able to paint on new features to old functions, adapt to a variety of error schemes (could even use classes like our current exceptions!) as needed for any user, not just a compiler-recognized magic type, and be able to write it reasonably conveniently.
November 22, 2020
On Mon, Nov 23, 2020 at 12:22:10AM +0000, Paul Backus via Digitalmars-d wrote: [...]
> In Herb Sutter's proposal, throwing an exception is syntax sugar for a return statement. There is no stack unwinding.

This is where I'd lean as well. Retain the same syntax, but change the underlying implementation.


T

-- 
Let's eat some disquits while we format the biskettes.
November 23, 2020
On Sunday, 22 November 2020 at 17:37:18 UTC, Roman Kashitsyn wrote:
> In the video from DConf 2020 about @live functions (https://youtu.be/XQHAIglE9CU) Walter mentioned that all @live functions are nothrow.  He also thinks that exceptions are obsolete.

I haven't watched the video yet, but in general I think we need some form of error handling that more or less have the same semantics as the existing one. There are other ways to implement what looks like exception. Just look at the error handling in Swift, Zig and the proposal for C++ [1]. I think without any form of language support and syntax sugar, error handling is going to be a pain.

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf

--
/Jacob Carlborg


« First   ‹ Prev
1 2 3 4