February 24, 2022
On 2/23/2022 11:23 PM, rikki cattermole wrote:
> 
> On 24/02/2022 6:05 PM, Walter Bright wrote:
>> One of the requirements I proposed for Phobos2 was for it to not use exceptions.
> 
> This is the sort of code we are going to end up with without a solution like value type exceptions. Explicit error types and returns ala @mustuse are not better than runtime exceptions, they are much worse for readability.

I agree, which makes this a challenge.

One solution is to design the return value so that failure is a valid part of the type. For example, searching for X can return an empty result, rather than a special "not found" error.

Another solution is to redesign the problem. For example, currently Phobos throws on an invalid Unicode character. A better way is to treat such as a "replacement character".
February 24, 2022
On Thu, Feb 24, 2022 at 11:14:39AM -0800, Walter Bright via Digitalmars-d wrote:
> On 2/23/2022 11:23 PM, rikki cattermole wrote:
> > 
> > On 24/02/2022 6:05 PM, Walter Bright wrote:
> > > One of the requirements I proposed for Phobos2 was for it to not use exceptions.
> > 
> > This is the sort of code we are going to end up with without a solution like value type exceptions. Explicit error types and returns ala @mustuse are not better than runtime exceptions, they are much worse for readability.
> 
> I agree, which makes this a challenge.
> 
> One solution is to design the return value so that failure is a valid part of the type. For example, searching for X can return an empty result, rather than a special "not found" error.
[...]

The problem with replacing exceptions with return codes is that it requires writing tons of boilerplate to handle error codes. I work with C at work every day, and eventually every function starts looking like this:

	int some_func(some_type_t *some_args) {
		int ret = SOME_FUNC_ERR;

		if ((ret = do_something(...)) != DO_SOMETHING_OK)
			goto EXIT;

		if ((ret = do_something_else(...)) != DO_SOMETHING_ELSE_OK)
			goto EXIT;

		if ((ret = do_yet_another(...)) != DO_YET_ANOTHER_OK)
			goto EXIT;

		// ... ad nauseaum

		ret = SOME_FUNC_OK;
	EXIT:
		return ret;
	}

Every single function call must be wrapped with `if ((ret = blah(bleh)) == bluh)` boilerplate, and every developer on the team inevitably reinvents their own system of error codes, so you have 1001 different representations of success and failure, and woe to you when you mix up one for the other because you'd have silently introduced a subtle bug.

And of course, given the painful amounts of boilerplate involved, people tend to get lazy so error codes are not checked, or don't get propagated correctly, etc..

Not to mention the proliferation of thrice-reinvented gratuitously incompatible lists of error codes across every module, and sometimes every function. Once you go past 1 or 2 levels in the call graph, the fine distinctions between these error codes get lost in translation, with the inevitable result that nobody uses any of the error codes except for "OK" and "INTERNAL_ERROR".  Every single possible failure in a complex subsystem of 26-level deep function calls eventually collapses onto a generic, useless "INTERNAL_ERROR", and good luck to the poor fool who has to debug where in the 50 or so files the problem is when the customer reports that the application said "internal error".


This is NOT where I want D to go.  Getting rid of exceptions is a non-solution, we're just going back to the bad ole days of manual error checking, gratuitous boilerplate, and incentivizing programmers to ignore / shove aside errors as much as possible 'cos error handling just Gets In The Way(tm).

What we should be doing instead is, free ourselves from the needless constraints of C++ exception handling, and implement a better mechanism for throwing exceptions. Leave C++ interop at the C++/D boundary; extern(D) code ought to be able to throw away libunwind and use a much more efficient implementation of exceptions than C++ does.


T

-- 
Latin's a dead language, as dead as can be; it killed off all the Romans, and now it's killing me! -- Schoolboy
February 24, 2022
On Thursday, 24 February 2022 at 13:36:29 UTC, Mike Parker wrote:
> On Thursday, 24 February 2022 at 13:28:23 UTC, Mike Parker wrote:
>> On Thursday, 24 February 2022 at 13:19:55 UTC, deadalnix wrote:
>>
>>> I don't see what from the discussion here and from the original document demonstrate exception to be a bad idea.
>>>
>>
>> I was just responding to forkit's "confusion" about the contradiction between Walter's current stance and his past one. I'll leave it to Walter to explain his current thinking, as I can't recall what he's said about it.
>
> Okay, here we go:
>
> https://www.youtube.com/watch?v=g26eJcs2QB0&t=0s

thanks. for those interested, it's 31 minutes into the talk:

https://youtu.be/g26eJcs2QB0?t=1809

From what I can grasp, Walter seems not too convinced about possible solutions to removing exceptions. His main gripe with exceptions seems to be -> "there are a lot of paths in your code that you've never thought about".

In any case, when looking at Herbs paper, I'm reminded of what Jon Skeet said about exceptions:

" ... in almost every situation where the performance of exceptions would be significant, there are bigger design problems to worry about."

and..

"When designing the contract of a method, it's worth considering whether it's reasonable to call that method in a fairly tight loop, continuing to call it even if the previous call fails. In that case, it may be worth having two methods, one of which uses exceptions to indicate errors and one of which doesn't (like Parse and TryParse) but in all other situations, using exceptions is unlikely to cause performance problems, but is likely to make the calling code much easier to understand.

https://jonskeet.uk/csharp/exceptions.html

Now I don't know too much about exception handling in C++, but personally, the above advice has always served me (and my end-users) well.

Replacing exceptions with some radical change, will need to be well justified, and nobody (as far as I can tell), has done that.


February 24, 2022
On Thursday, 24 February 2022 at 19:14:39 UTC, Walter Bright wrote:
>
> I agree, which makes this a challenge.
>
> One solution is to design the return value so that failure is a valid part of the type. For example, searching for X can return an empty result, rather than a special "not found" error.
>
> Another solution is to redesign the problem. For example, currently Phobos throws on an invalid Unicode character. A better way is to treat such as a "replacement character".

Exceptions are nice because they go under the hood and we can catch them ... if we want to. Problem of replacing with return values are for example indexing an array and the ArrayIndexError exception. 90% of the code we don't check for this as it would be a bug in our code but it's a good exception that enables us to quickly find the bug. How would it be possible to replace this with a return value? One that comes to mind is a tagged union. In this case we would want this to be handled silently and behave like an exception in readable code. .unwrap hell á la Rust is something I want to avoid. However, there are several other ways.

Exceptions from a readable code point of view is desirable, it's just that it might be bad for code generation and performance. Would should look into fixing this first before we start to design something completely new. If we design something completely new, then we should make it as similar as traditional exceptions.
February 24, 2022
On Thu, Feb 24, 2022 at 08:34:03PM +0000, IGotD- via Digitalmars-d wrote: [...]
> Exceptions are nice because they go under the hood and we can catch them ...  if we want to.

Exactly, it makes APIs cleaner and code more maintainable when you don't have to litter it with error-handling paraphrenalia all over the place. Just like how having a GC allows you to eliminate memory-management paraphrenalia that pollutes, e.g., every C/C++ API. The resulting code is cleaner, more readable/maintainable, and more easily composable.


[...]
> .unwrap hell á la Rust is something I want to avoid.

Yeah, that's a route I hope we would *not* take in our effort to replace exceptions.

I've said this before, but I suspect Walter's problem with exceptions isn't so much the concept of exceptions itself, but rather with the current implementation, which we inherited from C++. There's got to be a way of working with exception-like syntax and convenience without the performance hit *and* without the proliferation of error-code handling boilerplate.


T

-- 
Never wrestle a pig. You both get covered in mud, and the pig likes it.
February 24, 2022
On 2/24/2022 12:01 PM, H. S. Teoh wrote:
> The problem with replacing exceptions with return codes is that it
> requires writing tons of boilerplate to handle error codes.

I specifically said not using return codes, and gave examples.
February 24, 2022
On 2/24/2022 12:34 PM, IGotD- wrote:
> Exceptions are nice because they go under the hood and we can catch them ... if we want to. Problem of replacing with return values are for example indexing an array and the ArrayIndexError exception.

Array overflows are a "terminate the program" thing, as they are program bugs. Program bugs are not in the purview of exceptions.
February 24, 2022
On 2/24/2022 5:28 AM, Mike Parker wrote:
> I'll leave it to Walter to explain his current thinking, as I can't recall what he's said about it.


https://digitalmars.com/d/archives/digitalmars/D/OT_-_C_exceptions_are_becoming_more_and_more_problematic_358395.html#N358446
February 24, 2022
On Thu, Feb 24, 2022 at 02:42:14PM -0800, Walter Bright via Digitalmars-d wrote:
> On 2/24/2022 12:01 PM, H. S. Teoh wrote:
> > The problem with replacing exceptions with return codes is that it requires writing tons of boilerplate to handle error codes.
> 
> I specifically said not using return codes, and gave examples.

It amounts to the same thing: the caller must explicitly handle all error conditions. This is cumbersome and incentivises people to ignore error conditions or just sweep it under the rug.  And often, the caller simply isn't in the position to decide how to handle the error condition (for example, whether a particular error condition is fatal or not depends on contextual information that's only available further up the call stack).

More specifically: making failure a valid part of your return type requires that your callers explicitly check for this failure. It's just a paraphrasis of `if ((ret = func(...)) != OK) goto EXIT;`. Explicit checking is a good idea in some cases, but in other cases introduces unacceptable amounts of boilerplate, such as when every function call in a long list of function calls must be wrapped around `if (error) goto EXIT;` boilerplate.

Also, redesigning the problem like using the replacement character on invalid UTF-8 does not always make sense.  Sometimes you *want* your code to abort upon failure rather than silently substitute a nonce value in your data. E.g., if you're deep inside some parsing function and encounter a UTF-8 encoding problem, you do not want it to just insert some replacement data that isn't part of the input stream; you want it to bail out and abort the entire operation with a proper error message.


T

-- 
Marketing: the art of convincing people to pay for what they didn't need before which you fail to deliver after.
February 24, 2022
On Thursday, 24 February 2022 at 20:01:20 UTC, H. S. Teoh wrote:
> On Thu, Feb 24, 2022 at 11:14:39AM -0800, Walter Bright via Digitalmars-d wrote:
>> On 2/23/2022 11:23 PM, rikki cattermole wrote:
>> > 
>> > On 24/02/2022 6:05 PM, Walter Bright wrote:
>> > > One of the requirements I proposed for Phobos2 was for it to not use exceptions.
>> > 
>> > This is the sort of code we are going to end up with without a solution like value type exceptions. Explicit error types and returns ala @mustuse are not better than runtime exceptions, they are much worse for readability.
>> 
>> I agree, which makes this a challenge.
>> 
>> One solution is to design the return value so that failure is a valid part of the type. For example, searching for X can return an empty result, rather than a special "not found" error.
> [...]
>
> The problem with replacing exceptions with return codes is that it requires writing tons of boilerplate to handle error codes. I work with C at work every day, and eventually every function starts looking like this:
>
> 	int some_func(some_type_t *some_args) {
> 		int ret = SOME_FUNC_ERR;
>
> 		if ((ret = do_something(...)) != DO_SOMETHING_OK)
> 			goto EXIT;
>
> 		if ((ret = do_something_else(...)) != DO_SOMETHING_ELSE_OK)
> 			goto EXIT;
>
> 		if ((ret = do_yet_another(...)) != DO_YET_ANOTHER_OK)
> 			goto EXIT;
>
> 		// ... ad nauseaum
>
> 		ret = SOME_FUNC_OK;
> 	EXIT:
> 		return ret;
> 	}
>
> Every single function call must be wrapped with `if ((ret = blah(bleh)) == bluh)` boilerplate, and every developer on the team inevitably reinvents their own system of error codes, so you have 1001 different representations of success and failure, and woe to you when you mix up one for the other because you'd have silently introduced a subtle bug.
>
> And of course, given the painful amounts of boilerplate involved, people tend to get lazy so error codes are not checked, or don't get propagated correctly, etc..
>
> Not to mention the proliferation of thrice-reinvented gratuitously incompatible lists of error codes across every module, and sometimes every function. Once you go past 1 or 2 levels in the call graph, the fine distinctions between these error codes get lost in translation, with the inevitable result that nobody uses any of the error codes except for "OK" and "INTERNAL_ERROR".  Every single possible failure in a complex subsystem of 26-level deep function calls eventually collapses onto a generic, useless "INTERNAL_ERROR", and good luck to the poor fool who has to debug where in the 50 or so files the problem is when the customer reports that the application said "internal error".
>
>
> This is NOT where I want D to go.  Getting rid of exceptions is a non-solution, we're just going back to the bad ole days of manual error checking, gratuitous boilerplate, and incentivizing programmers to ignore / shove aside errors as much as possible 'cos error handling just Gets In The Way(tm).
>
> What we should be doing instead is, free ourselves from the needless constraints of C++ exception handling, and implement a better mechanism for throwing exceptions. Leave C++ interop at the C++/D boundary; extern(D) code ought to be able to throw away libunwind and use a much more efficient implementation of exceptions than C++ does.
>
>
> T

Nobody said to verbatim copy what C does, I personally explicitly said to take that idea and evolve it, just like the newer modern languages are properly doing (Go, Zig, Rust, Odin)

Tagged Unions, Multiple return type, enum/type inference, constructs to bubble up things etc.. we must move forward!

I suspect in the near future, exception handling will be seen as very bad practice, if not already, we must prepare..

We no longer live in an era with single cores!