www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A Philosophy of Software Design

reply Walter Bright <newshound2 digitalmars.com> writes:
by John Ousterhout

https://www.amazon.com/Philosophy-Software-Design-2nd/dp/173210221X

I did not know it, but I've been looking for this book for a long time. I'm 
about halfway through it.

It's about managing software complexity. Complexity is the bane of all 
significant software. Over the years I have battled it, and have circled into 
many of the techniques described in the book. Those who have seen my talks know 
that a recurring focus is how to use D to manage software complexity.

This book is an easy read, and only $10. It's worth far more.

For example, the title of Chapter 10 is "Define Errors out of Existence". I've 
been trying to do that since the 1980s.

Back in the 80's, a C compiler was required to support strings of at least NNN 
characters in length. So most C compilers would put a check in for that, and 
issue an error message on overflow, and then recover from the error.

My compiler took a different approach. It would just keep enlarging the string 
buffer until it ran out of memory. Then there was only one error message - "out 
of memory" - and the compiler would abort. This applied to all sorts of 
arbitrary limits the C spec placed on things. There was no need to add error 
recovery code.

But still, the D compiler is overly complex, and so is Phobos. We've still got
a 
lot to learn.

I've not vibe coded enough to see it for myself, but I've been told that AI 
generated code simply generates code until it works. It doesn't do very well at 
managing complexity. Something to watch out for.
May 23
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
The first sentence of the chapter "Define Errors Out Of Existence" says:

"Exception handling is one of the worst sources of complexity in software
systems."

I suspect that adding EH to D was a mistake. It certainly was a mistake to
build 
it into Unicode string handling. I'd like to see if we can figure a way to do 
Phobos without exception handling.
May 23
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/05/2026 1:42 PM, Walter Bright wrote:
 The first sentence of the chapter "Define Errors Out Of Existence" says:
 
 "Exception handling is one of the worst sources of complexity in 
 software systems."
 
 I suspect that adding EH to D was a mistake. It certainly was a mistake 
 to build it into Unicode string handling. I'd like to see if we can 
 figure a way to do Phobos without exception handling.
I've been attempting to find a way to do that for 3+ years. Unfortunately you made it so that we can't do value type exceptions. Aka the compiler converts the throw set + return type into a sum type. That requires inferring to work, and nothrow is now on the chopping block due to that. Which leaves us with result types and unwrapping. This is my solution to the problem (please ignore the unwrap alias stuff I did later on, I want to kill that but thats a political problem): https://forum.dlang.org/post/vcozmhysmztnmngzopnm forum.dlang.org This also handles errors without a result type.
May 23
parent reply Walter Bright <newshound2 digitalmars.com> writes:
Thanks. This is an interesting general purpose method.

But the book suggests a different approach in the title - define them out of 
existence. I've also thought of various general replacement schemes for
exceptions.

The difficulty with exceptions is they are complicated. It's difficult to
figure 
out what path the code execution goes through when there are various
exceptions. 
The exception paths are also a rich source of bugs because they are untested
and 
may never trigger.

Some examples of defining them out of existence:

1. The C Standard says the compiler must support n characters in a string 
literal. Implementing this involves detecting the overflow, issuing an error 
message, and then recovering. A define-out-of-existence approach, which I've 
always used, is just keep mallocing memory until the memory allocator fails - 
and then abort the program with "out of memory". You'll see this in the
compiler 
code.

2. Instead of throwing an exception when encountering an invalid code point,
the 
unicode handler should substitute in an "invalid character" code point. This 
behaves much like a floating point NaN value.

3. Invalid code nodes are replaced with "Error" nodes. This worked far better 
than I anticipated. No exceptions are thrown. It also behaves like NaN.
May 23
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/05/2026 4:10 PM, Walter Bright wrote:
 Thanks. This is an interesting general purpose method.
 
 But the book suggests a different approach in the title - define them 
 out of existence. I've also thought of various general replacement 
 schemes for exceptions.
 
 The difficulty with exceptions is they are complicated. It's difficult 
 to figure out what path the code execution goes through when there are 
 various exceptions. The exception paths are also a rich source of bugs 
 because they are untested and may never trigger.
Where possible yes, you should remove the possibility of needing to handle the error case. But you can't always do this especially for a standard library. It's far more on the rare side to have a problem where you can do it for and there are enough cases even in UTF/Unicode to want it to produce an error that you must handle. An awful lot of application development is based upon the principle of log + finalize outputs + kill task. And that requires some method of unwinding, either implicitly via EH or explicitly via result types. It is fairly well known and has been taught in software engineering for a long time to avoid unnecessary logic like error handling here. "Do any of your functions return error values? Is it possible to redefine those functions to eliminate the error conditions? Remember that when a function returns an error, that error must be handled - or mishandled - at every point of call." Writing Solid Code, Page 198, Copyright 1993. https://www.amazon.com/Writing-Solid-Code-Microsoft-Programming/dp/1556155514
May 23
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/05/2026 4:27 PM, Richard (Rikki) Andrew Cattermole wrote:
 On 24/05/2026 4:10 PM, Walter Bright wrote:
 Thanks. This is an interesting general purpose method.

 But the book suggests a different approach in the title - define them 
 out of existence. I've also thought of various general replacement 
 schemes for exceptions.

 The difficulty with exceptions is they are complicated. It's difficult 
 to figure out what path the code execution goes through when there are 
 various exceptions. The exception paths are also a rich source of bugs 
 because they are untested and may never trigger.
Where possible yes, you should remove the possibility of needing to handle the error case. But you can't always do this especially for a standard library. It's far more on the rare side to have a problem where you can do it for and there are enough cases even in UTF/Unicode to want it to produce an error that you must handle. An awful lot of application development is based upon the principle of log + finalize outputs + kill task. And that requires some method of unwinding, either implicitly via EH or explicitly via result types. It is fairly well known and has been taught in software engineering for a long time to avoid unnecessary logic like error handling here. "Do any of your functions return error values? Is it possible to redefine those functions to eliminate the error conditions? Remember that when a function returns an error, that error must be handled - or mishandled - at every point of call."  Writing Solid Code, Page 198, Copyright 1993. https://www.amazon.com/Writing-Solid-Code-Microsoft- Programming/dp/1556155514
Code Complete 2nd edition has three chapters about error handling, including one for exceptions and another for asserts. This includes a brief discussion on each of: return a neutral value (not a character), error codes, log/error message, kill process. Copyright 2004, Page 194, https://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670 This is taught at tertiary education for programming. My copy was bought because it was required reading. Note that CS courses may not cover this, but programming and software engineering qualifications should.
May 23
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/05/2026 5:02 PM, Richard (Rikki) Andrew Cattermole wrote:
 On 24/05/2026 4:27 PM, Richard (Rikki) Andrew Cattermole wrote:
 On 24/05/2026 4:10 PM, Walter Bright wrote:
 Thanks. This is an interesting general purpose method.

 But the book suggests a different approach in the title - define them 
 out of existence. I've also thought of various general replacement 
 schemes for exceptions.

 The difficulty with exceptions is they are complicated. It's 
 difficult to figure out what path the code execution goes through 
 when there are various exceptions. The exception paths are also a 
 rich source of bugs because they are untested and may never trigger.
Where possible yes, you should remove the possibility of needing to handle the error case. But you can't always do this especially for a standard library. It's far more on the rare side to have a problem where you can do it for and there are enough cases even in UTF/Unicode to want it to produce an error that you must handle. An awful lot of application development is based upon the principle of log + finalize outputs + kill task. And that requires some method of unwinding, either implicitly via EH or explicitly via result types. It is fairly well known and has been taught in software engineering for a long time to avoid unnecessary logic like error handling here. "Do any of your functions return error values? Is it possible to redefine those functions to eliminate the error conditions? Remember that when a function returns an error, that error must be handled - or mishandled - at every point of call."  Writing Solid Code, Page 198, Copyright 1993. https://www.amazon.com/Writing-Solid-Code-Microsoft- Programming/dp/1556155514
Code Complete 2nd edition has three chapters about error handling, including one for exceptions and another for asserts. This includes a brief discussion on each of: return a neutral value (not a character), error codes, log/error message, kill process. Copyright 2004, Page 194, https://www.amazon.com/Code-Complete-Practical-Handbook- Construction/dp/0735619670 This is taught at tertiary education for programming. My copy was bought because it was required reading. Note that CS courses may not cover this, but programming and software engineering qualifications should.
"If you have the luxury of designing an API, design it in such a way that it minimizes the amount of error handling that is required, if at all possible. In addition, try to design APIs so that failures are not potentially critical if they go unhandled. Otherwise, appropriate exception handling can help you ensure that no errors that go unhandled will propagate dangers error conditions. Use wrappers to convert functions that may fail with traditional error code, so that they instead use exception handling." - Secure Programming Cookbook, Copyright 2003, https://www.amazon.com/Secure-Programming-Cookbook-Cryptography-Authentication/dp/0596003943 The more appropriate solution for D, is to use a struct that wraps the value: ```d struct ErrorCode { int code; } ``` Treat this like any result type, with opCast and mustuse rather than using exception handling. Because it doesn't supply a value, you don't need the opUnwrapIfTrue in this particular case. Ideally we could swap 1:1 at the function prototype, but LLVM will error if you try to do this, so that is a potential improvement we could have.
May 23
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Microsoft is not wrong. You're not wrong, either.

But consider the copyright dates on the books: 1993, 2003, 2004. I know my
ideas 
on what is better certainly have evolved over the years. A number of problems 
have come up with exceptions. I suppose it's like macros - they are a great 
idea, until you've used them for 10 years, and then they don't look so good.

Let's take Unicode again. The current method in Phobos is autodecode, which 
turned out to be a bad idea. It also throws an exception on malformed code
points.

The first problem is you cannot use any string code in Phobos without
supporting 
exceptions and memory allocations. The second problem is throwing an exception 
is the wrong solution.

Consider displaying text in your editor. Do you want the editor to throw an 
exception if the text has bad code points in it? Do you want the browser to 
throw an exception if the html has bad code points in it? No. Better to render 
the bad code point as the invalid code point.

If you really, really need to throw an exception, run the text through a 
*separate* filter to throw when it sees an invalid code point.

(Personally, I really do not want my string handling functions throwing 
exceptions. Like searching for a substring - what good would an exception be?)

Anyhow, I recommend checking the book I mentioned. It's only ten bucks! I'm 
pretty sure it will be worth your while.
May 23
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/05/2026 5:57 PM, Walter Bright wrote:
 Microsoft is not wrong. You're not wrong, either.
My PoV is that you gotta use the right tool for the job. Each strategy has its own strengths and weaknesses, and all of them should be supported. As required.
 But consider the copyright dates on the books: 1993, 2003, 2004. I know 
 my ideas on what is better certainly have evolved over the years. A 
 number of problems have come up with exceptions. I suppose it's like 
 macros - they are a great idea, until you've used them for 10 years, and 
 then they don't look so good.
In this particular case, the ideas presented in those books haven't evolved since then. And the conclusion is to use a mix of strategies based upon what the code needs to do. I've done plenty of research over the past few years on the topic, and haven't found anything notable.
 Let's take Unicode again. The current method in Phobos is autodecode, 
 which turned out to be a bad idea. It also throws an exception on 
 malformed code points.
Funny that you mention that. "When a conversion algorithm encounters such unconvertible data, the usual practice is either to throw an exception or to use a defined substitution character to represent the unconvertible data. In the case of conversion to one of the encoding forms of the Unicode Standard, the substitution character is defined as U+FFFD REPLACEMENT CHARACTER. For conversion between different encoding forms of the Unicode Standard, “U+FFFD Substitution of Maximal Subparts” in Section 3.9, Unicode Encoding Forms defines a practice for the use of U+FFFD which is consistent with the W3C standard for encoding. It is useful to apply the same practice to the conversion from non-Unicode encodings to an encoding form of the Unicode Standard. This practice is more secure because it does not result in the conversion consuming parts of valid sequences as though they were invalid. It also guarantees at least one replacement character will occur for each instance of an invalid sequence in the original text. Furthermore, this practice can be defined consistently for better interoperability between different implementations of conversion." - Unicode standard 17, page 323, 5.22 U+FFFD Substitution in Conversion. https://www.unicode.org/versions/Unicode17.0.0/UnicodeStandard-17.0.pdf TLDR: exceptions and replacement characters are both nominal approaches in Unicode handling.
 The first problem is you cannot use any string code in Phobos without 
 supporting exceptions and memory allocations. The second problem is 
 throwing an exception is the wrong solution.
It is not necessarily the wrong decision, which is why its so predominant. We have had trouble with it sure, and we would like to change it. But the problem domain does suggest it /can/ be the right choice. Even if I personally wouldn't do it with exceptions. There are some very nice boundaries in the algorithms and plenty of opportunities for specialization of functions including of decoding for specific error handling requirements.
 Consider displaying text in your editor. Do you want the editor to throw 
 an exception if the text has bad code points in it? Do you want the 
 browser to throw an exception if the html has bad code points in it? No. 
 Better to render the bad code point as the invalid code point.
If that exception is escaping the editor component rendering and causing the program to crash, that is a MAJOR BUG. Exceptions are supposed to be caught and handled much more locally in components like this, if there is potential for them to be thrown. Under no circumstances should that exception impact the user beyond a error alert. You should try Intellij out some time, sadly the D plugin still produces exceptions but yet its still quite functional. When applications are written properly, they handle exceptions gracefully and don't just fail at the first bad input.
 If you really, really need to throw an exception, run the text through a 
 *separate* filter to throw when it sees an invalid code point.
 
 (Personally, I really do not want my string handling functions throwing 
 exceptions. Like searching for a substring - what good would an 
 exception be?)
 
 Anyhow, I recommend checking the book I mentioned. It's only ten bucks! 
 I'm pretty sure it will be worth your while.
I considered it, but the $70 price tag ended that thought process beyond adding to a wish list lol.
May 23
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/23/2026 11:18 PM, Richard (Rikki) Andrew Cattermole wrote:
 In this particular case, the ideas presented in those books haven't evolved 
 since then.
See the book I suggested, "A Philosophy of Software Design", which shows an evolved understanding.
 TLDR: exceptions and replacement characters are both nominal approaches in 
 Unicode handling.
I cannot say I automatically accept programming advice from the Unicode people. After all, they invented normalized code points, an astonishingly bad idea. I've never encountered anyone able to defend it beyond 2 rounds.
 The first problem is you cannot use any string code in Phobos without 
 supporting exceptions and memory allocations. The second problem is throwing 
 an exception is the wrong solution.
It is not necessarily the wrong decision, which is why its so predominant.
It was the wrong decision. Even Andrei came around on this, to his great credit.
 We have had trouble with it sure, and we would like to change it. But the 
 problem domain does suggest it /can/ be the right choice. Even if I personally 
 wouldn't do it with exceptions. There are some very nice boundaries in the 
 algorithms and plenty of opportunities for specialization of functions
including 
 of decoding for specific error handling requirements.
I'm not convinced. I've never found any utility in generating exceptions on Unicode decoding.
 If that exception is escaping the editor component rendering and causing the 
 program to crash, that is a MAJOR BUG. Exceptions are supposed to be caught
and 
 handled much more locally in components like this, if there is potential for 
 them to be thrown. Under no circumstances should that exception impact the
user 
 beyond a error alert. You should try Intellij out some time, sadly the D
plugin 
 still produces exceptions but yet its still quite functional. When
applications 
 are written properly, they handle exceptions gracefully and don't just fail at 
 the first bad input.
Yes, you can write Exception handling correctly and it will behave as required. But that's not the point. The point is it is an over-complication and exposes dirty laundry when trying to encapsulate the functionality of a module.
 I considered it, but the $70 price tag ended that thought process beyond
adding 
 to a wish list lol.
$9.99 for the Kindle version. $22.95 for paperback.
May 24
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
BTW, I ordered the 3 books. Thanks!
May 23
prev sibling next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 24/05/2026 4:10 PM, Walter Bright wrote:
 Invalid code nodes are replaced with "Error" nodes. This worked far 
 better than I anticipated. No exceptions are thrown. It also behaves 
 like NaN.
That's not actually how you handled it, although I'm sure it solved some problems much more easily. You handled it by checking the error count. In other words a global flag. https://github.com/dlang/dmd/blob/3c2ad72ad3be9c38c76569ccd1574e39fec2256d/compiler/src/dmd/typesem.d#L4787 https://github.com/dlang/dmd/blob/3c2ad72ad3be9c38c76569ccd1574e39fec2256d/compiler/src/dmd/globals.d#L368 If you can get an error node without an error message that is an ICE that should have been emitted but wasn't.
May 23
next sibling parent Basile B. <b2.temp gmx.com> writes:
On Sunday, 24 May 2026 at 06:49:28 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 24/05/2026 4:10 PM, Walter Bright wrote:
 Invalid code nodes are replaced with "Error" nodes. This 
 worked far better than I anticipated. No exceptions are 
 thrown. It also behaves like NaN.
That's not actually how you handled it, although I'm sure it solved some problems much more easily. You handled it by checking the error count. In other words a global flag. https://github.com/dlang/dmd/blob/3c2ad72ad3be9c38c76569ccd1574e39fec2256d/compiler/src/dmd/typesem.d#L4787 https://github.com/dlang/dmd/blob/3c2ad72ad3be9c38c76569ccd1574e39fec2256d/compiler/src/dmd/globals.d#L368 If you can get an error node without an error message that is an ICE that should have been emitted but wasn't.
I think a better example is when in expsema the type of an expression is set to Terror. 1. This avoids plenty of null checks 2. This poisons the AST, so that, for example, further conversions wont pass What you put links on is rather the system used for error recovery. Generally I find that error gagging is clumsy but that is casually required (`__traits(compiles)`, `typeof()`, selection in an overload set, SFINAE, etc.) But finally I dont really see how exceptions are related to compiler errors. I've never seen a compiler using them for notifying invalid input code. Invalid input code is unrelated to compiler "as a software" errors (e.g a input file that does not exist).
May 24
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/23/2026 11:49 PM, Richard (Rikki) Andrew Cattermole wrote:
 On 24/05/2026 4:10 PM, Walter Bright wrote:
 Invalid code nodes are replaced with "Error" nodes. This worked far better 
 than I anticipated. No exceptions are thrown. It also behaves like NaN.
That's not actually how you handled it, although I'm sure it solved some problems much more easily. You handled it by checking the error count. In other words a global flag.
What the Error nodes did was eliminate the cascade of error messages resulting from the compiler trying to recover by guessing what the user intended. In other words, the Error nodes were for error recovery. The global flag was not about error reporting or recovery.
May 26
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 24 May 2026 at 04:10:31 UTC, Walter Bright wrote:
 Some examples of defining them out of existence:

 1. The C Standard says the compiler must support n characters 
 in a string literal. Implementing this involves detecting the 
 overflow, issuing an error message, and then recovering. A 
 define-out-of-existence approach, which I've always used, is 
 just keep mallocing memory until the memory allocator fails - 
 and then abort the program with "out of memory". You'll see 
 this in the compiler code.

 2. Instead of throwing an exception when encountering an 
 invalid code point, the unicode handler should substitute in an 
 "invalid character" code point. This behaves much like a 
 floating point NaN value.

 3. Invalid code nodes are replaced with "Error" nodes. This 
 worked far better than I anticipated. No exceptions are thrown. 
 It also behaves like NaN.
This is one way to define exceptions out of existence: expand the range of possible output values to include new values that represent errors (e.g., NaNs, Error nodes, failure states of Result/Option types). This pushes responsibility for handling those new values onto downstream code, which now has to include checks for isNan, isError, and so on. The other way is to restrict the range of possible *inputs* to the function, so that the specific inputs that would cause an exception to be thrown are no longer possible. For example, you could have your string-handling function take a ValidatedString as its input instead of a plain string, and design the public API of ValidatedString to ensure that you can only create one from a string whose code points are all valid. This pushes the responsibility for handling invalid code points onto upstream code. What these approaches have in common is that neither one actually removes the need for error handling. They just change where the error is handled.
May 24
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 8:36 AM, Paul Backus wrote:
 This is one way to define exceptions out of existence: expand the range of 
 possible output values to include new values that represent errors (e.g.,
NaNs, 
 Error nodes, failure states of Result/Option types). This pushes
responsibility 
 for handling those new values onto downstream code, which now has to include 
 checks for isNan, isError, and so on.
 
 The other way is to restrict the range of possible *inputs* to the function,
so 
 that the specific inputs that would cause an exception to be thrown are no 
 longer possible. For example, you could have your string-handling function
take 
 a ValidatedString as its input instead of a plain string, and design the
public 
 API of ValidatedString to ensure that you can only create one from a string 
 whose code points are all valid. This pushes the responsibility for handling 
 invalid code points onto upstream code.
 
 What these approaches have in common is that neither one actually removes the 
 need for error handling. They just change where the error is handled.
The idea is to reduce complexity by not having separate error paths entwined through the code in visible and invisible ways. For example, printf has to deal with errors in stdout. That adds significant complexity to printf, with or without exceptions. I do understand that what I am writing here is unconventional and hence obviously wrong. I don't expect to convince anyone easily, just like I am unable to convince people that macros are a terrible misfeature :-/
May 24
parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sun, May 24, 2026 at 02:15:47PM -0700, Walter Bright via Digitalmars-d wrote:
[...]
 The idea is to reduce complexity by not having separate error paths
 entwined through the code in visible and invisible ways.
 
 For example, printf has to deal with errors in stdout. That adds
 significant complexity to printf, with or without exceptions.
 
 I do understand that what I am writing here is unconventional and
 hence obviously wrong. I don't expect to convince anyone easily, just
 like I am unable to convince people that macros are a terrible
 misfeature :-/
You convinced me that macros are a terrible idea. :-D Actually, you didn't need to do much convincing. Having experienced them myself, and having to work with them every now and then at work (which involves a (very) large mostly-C/C++ codebase), and also having written a winning entry for the IOCCC with a fair amount of macro abuse, I've seen what they're capable of, and also what horrors they can bring. It didn't take much to convince me there are better ways of solving problems. As far as error-handling is concerned, I find exceptions very useful in my own code, where they help to keep the main logic clear, and separate out the messy error-handling stuff into a separate place without cluttering the main algorithm. IME the more straightforward the main logic is, the less likely there are bugs. The more error-handling you have to insert inline, the more places there are for bugs to hide. Throwing an exception to abort a complex calculation is better than sprinkling tons of `if (value.isNaN)` all over your code, or whatever its equivalent is (error node, null value, etc). But if you're finding yourself having to write tons of catch blocks everywhere, then you're doing something wrong. Then it's no better than sprinkling `if (x is null)` or `if (x.isNaN)` everywhere. In general, I find that exceptions should mostly be used only to signal conditions worthy of aborting an operation entirely, cases where you basically print a message to the console and abort the process or similar. Less extreme conditions often don't deserve an exception. I'd even go so far as to say that if your catch block is more than just logging an error message and signalling failure (abort program, abort current operation, etc.), you're doing something wrong. T -- Sometimes the best solution to morale problems is just to fire all of the unhappy people. -- despair.com
May 24
prev sibling next sibling parent reply Dom Disc <dominikus scherkl.de> writes:
On Sunday, 24 May 2026 at 04:10:31 UTC, Walter Bright wrote:

 3. Invalid code nodes are replaced with "Error" nodes. This 
 worked far better than I anticipated. No exceptions are thrown. 
 It also behaves like NaN.
This is why I always suggested a NaN value for all types. For signed integers the unfortunate T.min (0x80..00) which has no positive counterpart is the perfect candidate for NaN. Unsigned types doesn't have such a good candidate - for sure, most calculations that try to prevent overflows are very unlikely to return 0xFF..FF as result, nevertheless this is a valid result. So defining T.max as NaN is possible but ugly.
May 25
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/25/2026 5:14 AM, Dom Disc wrote:
 This is why I always suggested a NaN value for all types. For signed integers 
 the unfortunate T.min (0x80..00) which has no positive counterpart is the 
 perfect candidate for NaN. Unsigned types doesn't have such a good candidate - 
 for sure, most calculations that try to prevent overflows are very unlikely to 
 return 0xFF..FF as result, nevertheless this is a valid result. So defining 
 T.max as NaN is possible but ugly.
Unfortunately, since integer arithmetic may use T.min, it's not going to work as a NaN value.
May 25
prev sibling parent Basile B. <b2.temp gmx.com> writes:
On Sunday, 24 May 2026 at 04:10:31 UTC, Walter Bright wrote:
 Thanks. This is an interesting general purpose method.

 But the book suggests a different approach in the title - 
 define them out of existence. I've also thought of various 
 general replacement schemes for exceptions.

 The difficulty with exceptions is they are complicated. It's 
 difficult to figure out what path the code execution goes 
 through when there are various exceptions. The exception paths 
 are also a rich source of bugs because they are untested and 
 may never trigger.

 Some examples of defining them out of existence:

 [...]

 3. Invalid code nodes are replaced with "Error" nodes. This 
 worked far better than I anticipated. No exceptions are thrown. 
 It also behaves like NaN.
Bollocks. Here you are mistalind software errors with how a compiler must handle wrong input. This third example is not a good one.
May 26
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, May 23, 2026 at 06:42:43PM -0700, Walter Bright via Digitalmars-d wrote:
 The first sentence of the chapter "Define Errors Out Of Existence"
 says:
 
 "Exception handling is one of the worst sources of complexity in
 software systems."
 
 I suspect that adding EH to D was a mistake. It certainly was a
 mistake to build it into Unicode string handling. I'd like to see if
 we can figure a way to do Phobos without exception handling.
On the contrary, you yourself said back in the day that exception handling was a good idea because it forced the program to stop upon encountering an I/O error. As opposed to a C program ignoring the return value of printf(), et al, and blindly barging forward when an unexpected condition like a disk full error started happening. T -- "If this is the solution, I want my precipitate back."
May 24
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 7:51 AM, H. S. Teoh wrote:
 On the contrary, you yourself said back in the day that exception
 handling was a good idea because it forced the program to stop upon
 encountering an I/O error.  As opposed to a C program ignoring the
 return value of printf(), et al, and blindly barging forward when an
 unexpected condition like a disk full error started happening.
Yes, I did say that. But as I also wrote, my thinking has evolved over time and experience. printf does two things: 1. format arguments into a character stream 2. write the stream to stdout. So, I have advocated the "sink" or "output range" approach, which would look like this: ``` printf(sink, format, args...); ``` printf shouldn't know anything about sink, it should just send the characters to it. You can see this in the dmd implementation, where OutBuffer is the sink for formatted characters, and ErrorSink is used to accept messages about errors. By consolidating output into sinks, one does not have to check for an error at every invocation of printf. I still use printf in dmd, but only for the porpoise of printing debugging information so I can debug the compiler. I use snprintf() for when things matter, because snprintf() accepts a (primitive) sink as an argument.
May 24
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sun, May 24, 2026 at 02:00:35PM -0700, Walter Bright via Digitalmars-d wrote:
 On 5/24/2026 7:51 AM, H. S. Teoh wrote:
 On the contrary, you yourself said back in the day that exception
 handling was a good idea because it forced the program to stop upon
 encountering an I/O error.  As opposed to a C program ignoring the
 return value of printf(), et al, and blindly barging forward when an
 unexpected condition like a disk full error started happening.
Yes, I did say that. But as I also wrote, my thinking has evolved over time and experience. printf does two things: 1. format arguments into a character stream 2. write the stream to stdout.
IMO, the problems stem from conflating these two distinct operations into one. Formatting a character stream doesn't have anything to do with writing to stdout. The former involves manipulating string data in-memory; the latter is involves interacting with the OS and doing I/O, which is always prone to errors and unexpected conditions. The correct approach is to separate the two operations, which is essentially what you propose:
 So, I have advocated the "sink" or "output range" approach, which
 would look like this:
 
 ```
 printf(sink, format, args...);
 ```
 
 printf shouldn't know anything about sink, it should just send the
 characters to it.
This is equivalent to separating concerns: printf is concerned with formatting and only formatting; writing the output is handled by the sink. This immediately cleans up the horrible mess in C of endless printf variants: fprintf, snprintf, vprintf, vfprintf, vsnprintf, ad nauseum. In fact, in D, we do have a separate .format() (.formattedWrite underneath) and .write. For convenience we have writef combine the two, but it's really no more than syntactic sugar. You could have just written: formattedWrite(stdout.lockingTextWriter, fmt, args...); The names could use some abbreviating, but the principle is sound.
 You can see this in the dmd implementation, where OutBuffer is the
 sink for formatted characters, and ErrorSink is used to accept
 messages about errors. By consolidating output into sinks, one does
 not have to check for an error at every invocation of printf.
But that just returns you to the original problem: if the sink carries error information, someone needs to check it. If nobody does, then the code just barges along thinking everything is fine when the sink is already in an error state. No different from C programs ignoring the return code of printf and doing silly things when e.g. the disk is full. And as we all know, programmers are lazy; if error-checking is optional, nobody will do it. And enforcement doesn't work either: if you force people to handle error conditions, they will just do the bare minimum they can to get away with it. Hence the proliferation of C functions that return -1 (or equivalent dummy value) for any and every error condition, with no indication of what actually went wrong. Which leads to an entire application getting into an error state from some obscure combination of conditions, and the only message you can get out of it is "Internal error".
 I still use printf in dmd, but only for the porpoise of printing
 debugging information so I can debug the compiler. I use snprintf()
 for when things matter, because snprintf() accepts a (primitive) sink
 as an argument.
(I'd love to be introduced to the porpoise that can help me debug compilers. But I digress. ;-) On a more serious note, I find D's .writefln a much better debugging tool than C's printf. Thanks to DbI, dumping the state of a complex type is often as simple as `writefln("%s", myBigStruct)`, as opposed to printf, where you have to manually spell out %-specifiers for every field of the struct you wish to examine. Combining writefln with UFCS chains makes it even more powerful: I often use it to debug code that handle multi-dimensional data; e.g., to dump 2D data stored in a 1D array, I can just write: ``` writefln("%-(%-(%s, %)\n%)", myFlatArray.chunks(myWidth)); ``` and instantly get nice output like this: ``` 1 2 3 4 5 6 7 8 9 ``` Try doing that in C, and you'll quickly find yourself spending an entire afternoon writing a debug module just to format debug info in a human-digestible way. In D, just a few seconds to write out the nested %-specifiers and you're on your way. T -- Obviously, some things aren't very obvious.
May 24
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 5:47 PM, H. S. Teoh wrote:
 And enforcement doesn't work either: if you force people to handle error
 conditions, they will just do the bare minimum they can to get away with
 it.  Hence the proliferation of C functions that return -1 (or
 equivalent dummy value) for any and every error condition, with no
 indication of what actually went wrong.  Which leads to an entire
 application getting into an error state from some obscure combination of
 conditions, and the only message you can get out of it is "Internal
 error".
You do have a point. However, it's much easier to put a single error check in the sink rather than in every single call to printf. (And this his how the sink works in dmd.) Keep in mind that the potential for an exception every single call will insert invisible unwinding code, which adds invisible complexity to your code. This is addressed in the referenced book.
 On a more serious note, I find D's .writefln a much better debugging
 tool than C's printf.
You're right, but my issue with writefln is even a simple call to it generates and absolute blizzard of template code emitted to the object file. Since much of my work involves looking at the assembler dump of the code, it becomes hard to find what I'm looking for in that blizzard. I've discussed this with Adam (Phobos2) and he agrees and will address it.
May 24
parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sun, May 24, 2026 at 11:36:17PM -0700, Walter Bright via Digitalmars-d wrote:
 On 5/24/2026 5:47 PM, H. S. Teoh wrote:
[...]
 On a more serious note, I find D's .writefln a much better debugging
 tool than C's printf.
You're right, but my issue with writefln is even a simple call to it generates and absolute blizzard of template code emitted to the object file. Since much of my work involves looking at the assembler dump of the code, it becomes hard to find what I'm looking for in that blizzard.
The current implementation of writefln leaves a lot of room for improvement. A single call with a simple "%s" format pulls in the machinery for a truckload of specialized formatting code, like floating-point formatting (which like all things IEEE is exceedingly complex), BigInt handling, ad nauseum. Retrospect is always 20/20, as they say, but what *should* have been done is that the format string should be a compile-time argument that's scanned at compile-time by CTFE, and transformed into a series of straightforward calls for each argument. For example, writing: ``` writefln("Value at (%d, %d) is %.02f", x, y, price); ``` should generate the equivalent of: ``` writeln("Value at ("); writeln(formatInt(x)); writeln(", "); writeln(formatInt(y)); writeln(") is "); writeln(formatFloat(price); ``` I.e., it should only pull in what's actually used, not the entire formatting subsystem for handling every conceivable D type.
 I've discussed this with Adam (Phobos2) and he agrees and will address
 it.
Let's hope it will not be another over-engineered solution. T -- Every time you make a typo, the errorists win.
May 25
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 26/05/2026 2:29 AM, H. S. Teoh wrote:
 On Sun, May 24, 2026 at 11:36:17PM -0700, Walter Bright via Digitalmars-d
wrote:
 On 5/24/2026 5:47 PM, H. S. Teoh wrote:
[...]
 On a more serious note, I find D's .writefln a much better debugging
 tool than C's printf.
You're right, but my issue with writefln is even a simple call to it generates and absolute blizzard of template code emitted to the object file. Since much of my work involves looking at the assembler dump of the code, it becomes hard to find what I'm looking for in that blizzard.
The current implementation of writefln leaves a lot of room for improvement. A single call with a simple "%s" format pulls in the machinery for a truckload of specialized formatting code, like floating-point formatting (which like all things IEEE is exceedingly complex), BigInt handling, ad nauseum. Retrospect is always 20/20, as they say, but what *should* have been done is that the format string should be a compile-time argument that's scanned at compile-time by CTFE, and transformed into a series of straightforward calls for each argument. For example, writing: ``` writefln("Value at (%d, %d) is %.02f", x, y, price); ``` should generate the equivalent of: ``` writeln("Value at ("); writeln(formatInt(x)); writeln(", "); writeln(formatInt(y)); writeln(") is "); writeln(formatFloat(price); ``` I.e., it should only pull in what's actually used, not the entire formatting subsystem for handling every conceivable D type.
 I've discussed this with Adam (Phobos2) and he agrees and will address
 it.
Let's hope it will not be another over-engineered solution.
There is a simple way to archive this, use IES. Give IES support for format strings, and a lot of these problems disappear. Not to mention we can kill off the entire f variants! But alas politics...
May 25
next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, May 26, 2026 at 02:34:28AM +1200, Richard (Rikki) Andrew Cattermole via
Digitalmars-d wrote:
 On 26/05/2026 2:29 AM, H. S. Teoh wrote:
[...]
 The current implementation of writefln leaves a lot of room for
 improvement.  A single call with a simple "%s" format pulls in the
 machinery for a truckload of specialized formatting code, like
 floating-point formatting (which like all things IEEE is exceedingly
 complex), BigInt handling, ad nauseum.
 
 Retrospect is always 20/20, as they say, but what *should* have been
 done is that the format string should be a compile-time argument that's
 scanned at compile-time by CTFE, and transformed into a series of
 straightforward calls for each argument.  For example, writing:
[...]
 There is a simple way to archive this, use IES.
What's IES? T -- If creativity is stifled by rigid discipline, then it is not true creativity.
May 25
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 26/05/2026 2:39 AM, H. S. Teoh wrote:
 On Tue, May 26, 2026 at 02:34:28AM +1200, Richard (Rikki) Andrew Cattermole
via Digitalmars-d wrote:
 On 26/05/2026 2:29 AM, H. S. Teoh wrote:
[...]
 The current implementation of writefln leaves a lot of room for
 improvement.  A single call with a simple "%s" format pulls in the
 machinery for a truckload of specialized formatting code, like
 floating-point formatting (which like all things IEEE is exceedingly
 complex), BigInt handling, ad nauseum.

 Retrospect is always 20/20, as they say, but what *should* have been
 done is that the format string should be a compile-time argument that's
 scanned at compile-time by CTFE, and transformed into a series of
 straightforward calls for each argument.  For example, writing:
[...]
 There is a simple way to archive this, use IES.
What's IES? T
https://dlang.org/spec/istring.html
May 25
prev sibling parent Dejan Lekic <dejan.lekic gmail.com> writes:
On Monday, 25 May 2026 at 14:34:28 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 There is a simple way to archive this, use IES.

 Give IES support for format strings, and a lot of these 
 problems disappear. Not to mention we can kill off the entire f 
 variants!
100% That would be _awesome_ !!
May 25
prev sibling next sibling parent reply Zz <zz zz.com> writes:
On Sunday, 24 May 2026 at 01:42:43 UTC, Walter Bright wrote:
 The first sentence of the chapter "Define Errors Out Of 
 Existence" says:

 "Exception handling is one of the worst sources of complexity 
 in software systems."

 I suspect that adding EH to D was a mistake. It certainly was a 
 mistake to build it into Unicode string handling. I'd like to 
 see if we can figure a way to do Phobos without exception 
 handling.
Might find this interesting. https://rm4n0s.github.io/posts/3-error-handling-challenge/ Zz
May 24
next sibling parent reply Kapendev <alexandroskapretsos gmail.com> writes:
On Sunday, 24 May 2026 at 17:15:12 UTC, Zz wrote:
 https://rm4n0s.github.io/posts/3-error-handling-challenge/
 It's an error code but in a tagged union with N cases.
The error to read a file in Odin: ```go Error :: union { General_Error, io.Error, runtime.Allocator_Error, sys_windows.System_Error, } ``` This has around 32 cases. I think the idea is fine sometimes, but ehh. Hard to tell what the error really is. Mostly an API issue in this case.
May 24
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 11:02 AM, Kapendev wrote:
 The error to read a file in Odin:
"OOOOdddiiiiinnnn!!!!" https://youtu.be/XMHts7ahWcY?t=153
May 24
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Sunday, 24 May 2026 at 17:15:12 UTC, Zz wrote:
 On Sunday, 24 May 2026 at 01:42:43 UTC, Walter Bright wrote:
 The first sentence of the chapter "Define Errors Out Of 
 Existence" says:

 "Exception handling is one of the worst sources of complexity 
 in software systems."

 I suspect that adding EH to D was a mistake. It certainly was 
 a mistake to build it into Unicode string handling. I'd like 
 to see if we can figure a way to do Phobos without exception 
 handling.
Might find this interesting. https://rm4n0s.github.io/posts/3-error-handling-challenge/ Zz
That's a good challenge, really. The problem I have with that however is very simple. Why the heck do you need a stack trace at runtime ? If you need a stack trace then it's probably cuz you have encountered a serious bug. Time to run the software in gdb you see.
May 28
parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Thu, May 28, 2026 at 08:50:57PM +0000, Basile B. via Digitalmars-d wrote:
 On Sunday, 24 May 2026 at 17:15:12 UTC, Zz wrote:
[...]
 https://rm4n0s.github.io/posts/3-error-handling-challenge/
 
 Zz
That's a good challenge, really. The problem I have with that however is very simple. Why the heck do you need a stack trace at runtime ? If you need a stack trace then it's probably cuz you have encountered a serious bug. Time to run the software in gdb you see.
[...] Running the program in gdb is a luxury you don't always have. I've had to fix segfault bugs that happen only in a customer's production environment, so (1) gdb access is not an option, not even remotely (this is running on embedded hardware); (2) the problem is not reproducible in my dev environment; (3) the customer is using a release build, so no symbols, only stack addresses and register contents at the time of the segfault; (4) the customer cannot run a custom debug build with debug hooks because this is a live production environment. Having a stack trace enabled me to trace the location of the crash (build the exact version the customer is running, map hex addresses to the disassembly of the executable in my build, trace through the assembly and map it back to the source code, then trace the code back up the chain of callers to determine where the bad value came from). Without that information, the bug would've remained open till this day. T -- There are two types of idiots. One type says "this is old and therefore bad", the other type says "this is new and therefore better". Beware of individuals who fall under both types. -- Crivens
May 28
prev sibling next sibling parent reply tony <tonytdominguez aol.com> writes:
On Sunday, 24 May 2026 at 01:42:43 UTC, Walter Bright wrote:
 The first sentence of the chapter "Define Errors Out Of 
 Existence" says:

 "Exception handling is one of the worst sources of complexity 
 in software systems."
Ignoring errors definitely reduces complexity, and seems to be the choice of a lot of people who don't like to use exceptions. But if someone is going to attempt to handle or report errors, how does exception handling make that process more complex than passing return values?
Jun 27
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 27 June 2026 at 19:17:11 UTC, tony wrote:
 On Sunday, 24 May 2026 at 01:42:43 UTC, Walter Bright wrote:
 The first sentence of the chapter "Define Errors Out Of 
 Existence" says:

 "Exception handling is one of the worst sources of complexity 
 in software systems."
Ignoring errors definitely reduces complexity, and seems to be the choice of a lot of people who don't like to use exceptions. But if someone is going to attempt to handle or report errors, how does exception handling make that process more complex than passing return values?
Its still a bad goto with a "comefrom" statement, ideally d wouldve been written with first class multiple returns but doing that well may have needed to be 30 years ago when walter was deciding on how to extend c
Jun 27
prev sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Jun 27, 2026 at 07:17:11PM +0000, tony via Digitalmars-d wrote:
 On Sunday, 24 May 2026 at 01:42:43 UTC, Walter Bright wrote:
 The first sentence of the chapter "Define Errors Out Of Existence" says:
 
 "Exception handling is one of the worst sources of complexity in
 software systems."
 
Ignoring errors definitely reduces complexity, and seems to be the choice of a lot of people who don't like to use exceptions.
The correct understanding is to reduce complexity by rephrasing your problem such that the number of exceptional cases is minimized. Unfortunately, real world problems are messy and will never be completely free of exceptional conditions. The question then becomes, how do you handle those exceptional conditions in such a way that minimizes the amount of complexity added to handle them?
 But if someone is going to attempt to handle or report errors, how
 does exception handling make that process more complex than passing
 return values?
IMO it's situation-specific. Expanding the domain and range of your function to include exceptional cases as part of "normal" operation, such as sqrt() returning NaN for negative arguments (instead of throwing an exception, say), can reduce complexity in some ways (you don't have to worry about catching exceptions every time you call sqrt()). OTOH, it merely shoves the problem elsewhere: now you cannot rely on your values being real values: NaN poisons all subsequent calculations and now downstream code has to worry about what to do if the input is NaN. E.g., sqrt() is involved in some calculation that eventually ends up as the dimensions of some on-screen object. Now your rendering code has to deal with the problem of how to draw an object with NaN dimensions. (Complexity added: an extra NaN check in render() to prevent it from going off the rails when it tries to loop from 0 to NaN. Or extra NaN checks somewhere in-between to prevent it from getting into the render queue and reaching the render function in the first place. Either way you haven't eliminated complexity, merely shoved it into a different form which has to be handled elsewhere.) Sometimes throwing an exception *is* the least complex way to deal with the problem. In opening a file, for example, instead of throwing an exception, you *could* return a dummy object in place of a file if the file doesn't exist, or the filesystem is corrupt, or some other error occurs. Yes you got rid of a throw statement, but at what cost? Now downstream code has to deal with a dummy file object. Does it continue processing it as a normal file of 0 size? What if the program needs to differentiate between "file not found" vs. "filesystem is corrupt"? What if an empty file is valid input, and you need to differentiate that from an error condition? Somewhere down the line, *somebody* has to deal with the exceptional condition somehow. A single throw statement is less complexity than a bunch of special code to differentiate between an empty file and a dummy file representing an error state. T -- In order to understand recursion you must first understand recursion.
Jun 27
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/27/2026 12:44 PM, H. S. Teoh wrote:
 OTOH, it merely shoves the problem elsewhere: now you cannot rely on your
 values being real values: NaN poisons all subsequent calculations and
 now downstream code has to worry about what to do if the input is NaN.
The choice is: 1. the answer is NaN, so you know there is a problem with the code 2. the answer is a number, so you don't know if there is a problem with the code I choose (2) as the preferable option.
 E.g., sqrt() is involved in some calculation that eventually ends up as
 the dimensions of some on-screen object.  Now your rendering code has to
 deal with the problem of how to draw an object with NaN dimensions.
 (Complexity added: an extra NaN check in render() to prevent it from
 going off the rails when it tries to loop from 0 to NaN.  Or extra NaN
 checks somewhere in-between to prevent it from getting into the render
 queue and reaching the render function in the first place. Either way
 you haven't eliminated complexity, merely shoved it into a different
 form which has to be handled elsewhere.)
I prefer errors to be obvious rather than papered over.
 Sometimes throwing an exception *is* the least complex way to deal with
 the problem.  In opening a file, for example, instead of throwing an
 exception, you *could* return a dummy object in place of a file if the
 file doesn't exist, or the filesystem is corrupt, or some other error
 occurs.  Yes you got rid of a throw statement, but at what cost?  Now
 downstream code has to deal with a dummy file object.  Does it continue
 processing it as a normal file of 0 size?  What if the program needs to
 differentiate between "file not found" vs. "filesystem is corrupt"?
 What if an empty file is valid input, and you need to differentiate that
 from an error condition?  Somewhere down the line, *somebody* has to
 deal with the exceptional condition somehow.  A single throw statement
 is less complexity than a bunch of special code to differentiate between
 an empty file and a dummy file representing an error state.
A reasonable question. Consider: ```d err = fprintf(stdout,"hello "); if (err) handleIt(); err = fprintf(stdout,"betty\n"); if (err) handleIt(); ``` as opposed to: ```d fprintf(stdout,"hello "); fprintf(stdout,"betty\n"); if (stdout.isError) handleIt(); ``` Which would you prefer? (The downstream code shouldn't have to deal with a dummy file object. It should just deal with the file object as if it succeeded. The file object in an error state would simply do nothing.) I know I'm pushing upstream with this notion. I know it is not the conventional wisdom. But it has a lot of potential to significantly simplify code and eliminate a lot of untested error paths. It is worth looking into.
Jun 27
next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Jun 27, 2026 at 07:35:23PM -0700, Walter Bright via Digitalmars-d wrote:
 On 6/27/2026 12:44 PM, H. S. Teoh wrote:
[...]
 E.g., sqrt() is involved in some calculation that eventually ends up
 as the dimensions of some on-screen object.  Now your rendering code
 has to deal with the problem of how to draw an object with NaN
 dimensions.  (Complexity added: an extra NaN check in render() to
 prevent it from going off the rails when it tries to loop from 0 to
 NaN.  Or extra NaN checks somewhere in-between to prevent it from
 getting into the render queue and reaching the render function in
 the first place. Either way you haven't eliminated complexity,
 merely shoved it into a different form which has to be handled
 elsewhere.)
I prefer errors to be obvious rather than papered over.
One could also argue that it's preferable for the error path to be obvious, rather than implicit in the propagation of NaNs through code paths that handle non-error computation. For example, you get a NaN result, now you have to find the bug. Where did the NaN come from? Nobody knows, it could be anywhere from the first computation that might produce a NaN, to the end of the program where the NaN is observed. Not useful for actually finding and fixing the bug. You have to spend time to locate the point of failure. Whereas with an exception, you immediately know exactly where the point of failure is: the exception carries with it file and and line number information.
 Sometimes throwing an exception *is* the least complex way to deal
 with the problem.  In opening a file, for example, instead of
 throwing an exception, you *could* return a dummy object in place of
 a file if the file doesn't exist, or the filesystem is corrupt, or
 some other error occurs.  Yes you got rid of a throw statement, but
 at what cost?  Now downstream code has to deal with a dummy file
 object.  Does it continue processing it as a normal file of 0 size?
 What if the program needs to differentiate between "file not found"
 vs. "filesystem is corrupt"?  What if an empty file is valid input,
 and you need to differentiate that from an error condition?
 Somewhere down the line, *somebody* has to deal with the exceptional
 condition somehow.  A single throw statement is less complexity than
 a bunch of special code to differentiate between an empty file and a
 dummy file representing an error state.
A reasonable question. Consider: ```d err = fprintf(stdout,"hello "); if (err) handleIt(); err = fprintf(stdout,"betty\n"); if (err) handleIt(); ``` as opposed to: ```d fprintf(stdout,"hello "); fprintf(stdout,"betty\n"); if (stdout.isError) handleIt(); ``` Which would you prefer?
I prefer an exception-based mechanism: ```d try { writeln("hello"); writeln("betty"); ... } catch (Exception e) { handleIt(); } ``` Not much different from your 2nd variant, with the advantage that the Exception object tells you exactly where the problem happened, whereas stdout.isError leaves you guessing, where did the problem happen? Nobody knows, you have to do detective work before you can start fixing the problem.
 (The downstream code shouldn't have to deal with a dummy file object.
 It should just deal with the file object as if it succeeded. The file
 object in an error state would simply do nothing.)
That's what I was getting at. So you have code that continues running as if the file is not in an error state. Like floating-point calculations that absorb a NaN and continue computation as if nothing was wrong, only each time NaN propagates to the result. You don't know until the end that a problem has occurred earlier. In the case of NaNs, it's not so bad because a NaN in the output is an obvious indicator of a problem. With a file in an error state, the program may continue running in ignorance of the problem until the end, and if the programmer was careless the program might even declare success, only when you look at the output file it's empty (because the file object was in an error state so all operations were no-ops). And now you're scratching your head as to why the program produced an empty file instead of the expected output.
 I know I'm pushing upstream with this notion. I know it is not the
 conventional wisdom. But it has a lot of potential to significantly
 simplify code and eliminate a lot of untested error paths. It is worth
 looking into.
I'm not saying that your idea has no merit. My point is that it depends. For some cases, yes, having a NaN (or equivalent) to poison the computations help you simplify your code. I wouldn't want to have to check for an error state after every single floating-point operation, for example; that would be unreasonably onerous. But in other situations, like opening an output file, I *do* want to know immediately if there's a problem, instead of having the program blindly running all the way to the end and perhaps even deciding that output was produced successfully, only for the user to discover afterwards that the output file is empty or missing. T -- "Computer Science is no more about computers than astronomy is about telescopes." -- E.W. Dijkstra
Jun 27
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Yesterday, I watched an episode of "Aviation Disasters". In it, the airplane
had 
two pitot tubes for measuring airspeed. One fed the airspeed indicator for the 
pilot, the other fed the copilot's indicator.

The pilot's one started behaving erratically, showing a very different airspeed 
than the copilot's. There was no indication of which one was wrong (or if both 
were wrong). The pilot decided to trust his indicator.

The result was a crash and everybody died.

If the pilot's indicator said "NaN" then he'd have known which one was broken.
Jun 27
parent reply Forum User <forumuser example.com> writes:
On Sunday, 28 June 2026 at 03:49:06 UTC, Walter Bright wrote:
 Yesterday, I watched an episode of "Aviation Disasters". In it, 
 the airplane had two pitot tubes for measuring airspeed. One 
 fed the airspeed indicator for the pilot, the other fed the 
 copilot's indicator.

 The pilot's one started behaving erratically, showing a very 
 different airspeed than the copilot's. There was no indication 
 of which one was wrong (or if both were wrong). The pilot 
 decided to trust his indicator.

 The result was a crash and everybody died.

 If the pilot's indicator said "NaN" then he'd have known which 
 one was broken.
Air France 447? They were not broken but some were clogged and the speed returned differed. If the computer could magically have diagnosed which of the tubes "were broken" it could have simply excluded the "broken" from the air speed computation.
Jun 28
parent Walter Bright <newshound2 digitalmars.com> writes:
On 6/28/2026 1:09 AM, Forum User wrote:
 Air France 447? They were not broken but some were clogged and
 the speed returned differed. If the computer could magically
 have diagnosed which of the tubes "were broken" it could have
 simply excluded the "broken" from the air speed computation.
It was Birgenair Flight 301. The investigators guessed that the pitot tubes were blocked, but could not confirm it because they could not recover them. Nevertheless, the pitot tube on takeoff gave an indicated speed of 0. This should have been enough to trigger a nan. In general, the computer monitoring the instruments should be able to detect when one instrument is bad, simply because its reading is inconsistent with the other state of the airplane.
Jun 29
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/27/2026 8:30 PM, H. S. Teoh wrote:
 Whereas with an exception, you immediately know exactly where the point
 of failure is: the exception carries with it file and and line number
 information.
If it's a fatal error, that's ok. But if you need to "fix and continue", then exception unwinding has proved to be a problem due to hidden and probably untested code paths. It also makes it complicated for the programmer to figure out if these hidden paths are correct or not.
 I prefer an exception-based mechanism:
 
 ```d
 try {
 	writeln("hello");
 	writeln("betty");
 	...
 } catch (Exception e) {
 	handleIt();
 }
 ```
 
 Not much different from your 2nd variant, with the advantage that the
 Exception object tells you exactly where the problem happened,
It doesn't tell you which of the two writeln()s failed.
 That's what I was getting at.  So you have code that continues running
 as if the file is not in an error state.  Like floating-point
 calculations that absorb a NaN and continue computation as if nothing
 was wrong, only each time NaN propagates to the result.  You don't know
 until the end that a problem has occurred earlier.  In the case of NaNs,
 it's not so bad because a NaN in the output is an obvious indicator of a
 problem.  With a file in an error state, the program may continue
 running in ignorance of the problem until the end, and if the programmer
 was careless the program might even declare success, only when you look
 at the output file it's empty (because the file object was in an error
 state so all operations were no-ops).  And now you're scratching your
 head as to why the program produced an empty file instead of the
 expected output.
That is indeed the idea, but the trouble comes from the non-trivial complexity that results in unknown and untested execution paths.
 I'm not saying that your idea has no merit.  My point is that it
 depends.  For some cases, yes, having a NaN (or equivalent) to poison
 the computations help you simplify your code.  I wouldn't want to have
 to check for an error state after every single floating-point operation,
 for example; that would be unreasonably onerous.  But in other
 situations, like opening an output file, I *do* want to know immediately
 if there's a problem, instead of having the program blindly running all
 the way to the end and perhaps even deciding that output was produced
 successfully, only for the user to discover afterwards that the output
 file is empty or missing.
Yes, it would be poor design for the result to say it was "successful" when it is in an error state. A better design would be to ask it at the end if it succeeded, and if it did not succeed, provide the cache any diagnostic information.
Jun 27
next sibling parent reply Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
On Sunday, 28 June 2026 at 04:05:34 UTC, Walter Bright wrote:
 I prefer an exception-based mechanism:
 
 ```d
 try {
 	writeln("hello");
 	writeln("betty");
 	...
 } catch (Exception e) {
 	handleIt();
 }
 ```
 
 Not much different from your 2nd variant, with the advantage 
 that the
 Exception object tells you exactly where the problem happened,
It doesn't tell you which of the two writeln()s failed.
You'd typically pass this info in exception itself, therefore getting file and number, and runtime values causing this.
Jun 28
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/28/2026 5:14 AM, Alexandru Ermicioi wrote:
 You'd typically pass this info in exception itself, therefore getting file and 
 number, and runtime values causing this.
There wouldn't be an exception thrown if: ```d double d; ``` default initializes it to 0.0 instead of NaN. You will never have an indication that the calculation produces the wrong value.
Jun 29
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 29/06/2026 7:58 PM, Walter Bright wrote:
 On 6/28/2026 5:14 AM, Alexandru Ermicioi wrote:
 You'd typically pass this info in exception itself, therefore getting 
 file and number, and runtime values causing this.
There wouldn't be an exception thrown if: ```d double d; ``` default initializes it to 0.0 instead of NaN. You will never have an indication that the calculation produces the wrong value.
If it propergates yes, you need a read barrier check and throw if NaN. But you can also throw static analysis at it to catch royally stupid cases like so: ```d void thing(float arg) { float f; return f + arg * 2; } ```
Jun 29
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/29/2026 1:05 AM, Richard (Rikki) Andrew Cattermole wrote:
 But you can also throw static analysis at it to catch royally stupid cases
like so:
 
 ```d
 void thing(float arg) {
      float f;
 
      return f + arg * 2;
 }
 ```
Default initializing f to nan catches it and propagates the error. Default initialization to 0 does not.
Jun 29
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 30/06/2026 7:49 AM, Walter Bright wrote:
 On 6/29/2026 1:05 AM, Richard (Rikki) Andrew Cattermole wrote:
 But you can also throw static analysis at it to catch royally stupid 
 cases like so:

 ```d
 void thing(float arg) {
      float f;

      return f + arg * 2;
 }
 ```
I just noticed that is void and should be float oops.
 Default initializing f to nan catches it and propagates the error. 
 Default initialization to 0 does not.
At _runtime_ yes. But it shouldn't get that far for royally stupid code like this. Due to: 1. NaN like this should be considered to be a poison value, that will optimize _any_ expression that touches it by a modern backend. 2. It will NEVER do something useful at runtime. It will always a be a bug in the code, due to not initializing or not knowing that it is NaN not 0. 3. Failing to compile for 100% bad code is always better than runtime bad data. Even if it is a sentinel like NaN. ldc -O: ```d float thing(float arg) { float f; return f + arg * 2; } float another(float arg) { return thing(arg) + 2; } ``` ``` .LCPI0_0: .long 0x7fc00000 float example.thing(float): movss xmm0, dword ptr [rip + .LCPI0_0] ret .LCPI1_0: .long 0x7fc00000 float example.another(float): movss xmm0, dword ptr [rip + .LCPI1_0] ret ```
Jun 29
parent Walter Bright <newshound2 digitalmars.com> writes:
Relying on DFA to catch null and nan values will only catch a very small number 
of cases. It is nice to catch them, but it is not a sufficient substitute for 
robust dealing with errors.

BTW, dmd's optimizer will catch:

```d
int* p = null;
*p = 3;
```
Jul 03
prev sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Jun 27, 2026 at 09:05:34PM -0700, Walter Bright via Digitalmars-d wrote:
 On 6/27/2026 8:30 PM, H. S. Teoh wrote:
 Whereas with an exception, you immediately know exactly where the
 point of failure is: the exception carries with it file and and line
 number information.
If it's a fatal error, that's ok. But if you need to "fix and continue", then exception unwinding has proved to be a problem due to hidden and probably untested code paths. It also makes it complicated for the programmer to figure out if these hidden paths are correct or not.
I thought that's what unittest blocks were for? ;)
 I prefer an exception-based mechanism:
 
 ```d
 try {
 	writeln("hello");
 	writeln("betty");
 	...
 } catch (Exception e) {
 	handleIt();
 }
 ```
 
 Not much different from your 2nd variant, with the advantage that
 the Exception object tells you exactly where the problem happened,
It doesn't tell you which of the two writeln()s failed.
Sure it does. The stacktrace in e tells you exactly which one failed. [...]
 I'm not saying that your idea has no merit.  My point is that it
 depends.  For some cases, yes, having a NaN (or equivalent) to
 poison the computations help you simplify your code.  I wouldn't
 want to have to check for an error state after every single
 floating-point operation, for example; that would be unreasonably
 onerous.  But in other situations, like opening an output file, I
 *do* want to know immediately if there's a problem, instead of
 having the program blindly running all the way to the end and
 perhaps even deciding that output was produced successfully, only
 for the user to discover afterwards that the output file is empty or
 missing.
Yes, it would be poor design for the result to say it was "successful" when it is in an error state. A better design would be to ask it at the end if it succeeded, and if it did not succeed, provide the cache any diagnostic information.
That does not solve the problem of locating the point of failure. You check at the end of the program whether stdout is in an error state, and it says yes. Now what? When did it get into that state? It's anybody's guess. Whereas if File.open() had thrown an Exception, it would have carried file + line number information with it and you'd know immediately where the failure happened. T -- Why did the bear dissolve in water? Because it was a polar bear.
Jun 28
parent Walter Bright <newshound2 digitalmars.com> writes:
On 6/28/2026 8:03 AM, H. S. Teoh wrote:
 That does not solve the problem of locating the point of failure.
Yes, it can. The stdout can store that information and then carry on. At the end you can query it.
Jun 29
prev sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 28/06/2026 2:35 PM, Walter Bright wrote:
 A reasonable question. Consider:

 ```d
 err = fprintf(stdout,"hello ");
 if (err)
      handleIt();
 err = fprintf(stdout,"betty\n");
 if (err)
      handleIt();
 ```
 as opposed to:
 ```d
 fprintf(stdout,"hello ");
 fprintf(stdout,"betty\n");
 if (stdout.isError)
      handleIt();
 ```
 Which would you prefer?
The first. https://pubs.opengroup.org/onlinepubs/9699919799.orig/functions/fprintf.html - EILSEQ: A wide-character code that does not correspond to a valid character has been detected. - EINVAL: There are insufficient arguments. - For snprintf EOVERFLOW: The value of n is greater than {INT_MAX} or the number of bytes needed to hold the output excluding the terminating null is greater than {INT_MAX}. Wait until you see what the underlying write call has to deal with: https://pubs.opengroup.org/onlinepubs/9699919799.orig/functions/fputc.html# This is what I deal with, and saying "go fix your kernel" is not a valid answer. They are all like this, and you're lucky when most of the error cases are even defined like this. However neither is how I'd solve this if I could define the API. ```d void myPrintff(FILE*, ref int error, ...) { if (error) return; ... } int error; myPrintff(stdout, error, "hello "); myprintff(stdout, error, "betty\n"); if (error) handleIt(); ``` Or (this is the best out of the ways): ```d struct BorrowedFile { private bool haveError; private bool checkedError; File file; void printf(...); bool handleError() { checkedError = true; return haveError; } void printf(...) { checkedError = false; ... } ~this() { if (!checkedError) throw Exception("did not handle error"); } } BorrowedFile output = stdout.borrow(); output.printf("hello "); output.printf("betty\n"); if (output.handleError) // missing static analysis to guarantee this handleIt(); ``` So an explanation, C standard IO is backed by pipes, they are inherently non-transactional. Pipes may complete in an incomplete fashion or not at all, they may fail to send for one call, but the next identical one succeed. But even this isn't even remotely good for how BorrowedFile is being called, because it ignores one key factor. One call can be cheap, but the next could be expensive to get the arguments for, it may even have side effects that you don't want if it failed. The only solution to this is to... check the error. Although it could be more selective where. ```d BorrowedFile output = stdout.borrow(); output.printf("hello "); if (output.handleError) return handleIt1(); output.printf(expensiveWithSideEffects()); if (output.handleError) // missing static analysis to guarantee this handleIt2(); ``` The by-ref error state variable can be used to great effect, I certainly have. But you have to be quite careful with it. In above case its only used for logging/output, that isn't too bad. But there are plenty of other cases where you are NOT doing that! Its being used for actual logic that feeds back in. And you can't ignore that the error occurred as you can then get wrong results. Basically early returns can be desirable instead via an exception. For the static analysis capability its basically a combination of mustuse and result types force a check before a get. Where the destructor is considered a get. Easy enough to implement. Another solution that by-passes almost all of these problems, that PhobosV3 is going in the direction of: ```d string text = "betty"; int age = 2; StringBuilder builder = new StringBuilder; builder ~= i"hello $text you are ${age : 03d} old\n"; // throw on failure consolePrint(builder.get); ``` And yes, that is real code that should compile once we are done. But doesn't right now.
 I know I'm pushing upstream with this notion. I know it is not the 
 conventional wisdom.
Unconventional? I don't think that matches my polite way to describe it. Please read: 1. Mechanisms for compile-time enforcement of security - https://dl.acm.org/doi/10.1145/567067.567093 2. Typestate: A programming language concept for enhancing software reliability - https://ieeexplore.ieee.org/document/6312929 3. Typestate-oriented programming - https://dl.acm.org/doi/10.1145/1639950.1640073 4. Typestates for Objects - https://www.microsoft.com/en-us/research/publication/typestates-for-objects/ 5. Linear Effects, Exceptions, and Resource Safety - https://arxiv.org/html/2510.23517 6. Lightweight monadic regions - https://dl.acm.org/doi/10.1145/1411286.1411288 Number 3, 5, and 6 talk about resources like files specifically.
 But it has a lot of potential to significantly
 simplify code and eliminate a lot of untested error paths. It is worth
 looking into.
That's actually your fault that they are untested in D. This isn't the users fault ;) With checked exceptions, where you know which expression, scope and function can emit a given exception you can know if its a valid code path. There is simply no way to model the code paths and its not fixable as part of dmd's type system. The reason the fast dfa engine cannot analyze catch statements is because of this.
Jun 27
prev sibling parent reply Jon Degenhardt <jondegenhardt nowhere.com> writes:
On Sunday, 24 May 2026 at 01:42:43 UTC, Walter Bright wrote:
 The first sentence of the chapter "Define Errors Out Of 
 Existence" says:

 "Exception handling is one of the worst sources of complexity 
 in software systems."

 I suspect that adding EH to D was a mistake. It certainly was a 
 mistake to build it into Unicode string handling. I'd like to 
 see if we can figure a way to do Phobos without exception 
 handling.
A old paper relevant to error handling is [END-TO-END ARGUMENTS IN SYSTEM DESIGN by J.H. Saltzer, D.P. Reed, D.D. Clark](https://web.mit.edu/Saltzer/www/publications/endtoend/endtoend.pdf). It uses OS/network level examples, but it applies much more broadly. The paper argues that proper error handling behavior is only known at the application layer, not in low level modules. It points out the challenge with implementing error handling only at the application layer as being one of efficiency. However, it's easy to extend these challenges to those involving error handling at the programming language layer. If only the application knows how to handle errors, what options do reusable library routines have for supporting application needs? It's non-trivial. Consider reading unicode line by line from an input source. At the application level, there are several strategies (that I am aware of) an application might want to use when encountering invalid unicode bytes in the input. 1. Replace - Replace the invalid byte sequence with a Unicode replacement character (U+FFFD) 1. Remove - Like Replace, but don't include the replacement character. 1. Passthrough - Leave the offending bytes in place. Only applies when the input line is type valid for raw bytes. That is, it's not applicable if reading to a string type requiring or assuming valid unicode characters. 1. Drop the line - Since this application reading line-by-line. Note that in this case the application may also want to signal or record that individual lines have been dropped. 1. Stop the program - And take some action, like informing a user. One challenge is that the application level program may be many layers of code away from the line reader. Designing a low level, general purpose library that supports a fair variety of error handling scenarios is difficult. The exception support or not question plays into this, as exceptions are one way to support several of these application scenarios. Unicode error handling is worth attention because of all the trouble it's been for D. But it comes up in other places as well. --Jon
Jun 28
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/28/2026 10:16 PM, Jon Degenhardt wrote:
 Unicode error handling is worth attention because of all the trouble it's been 
 for D.
The trouble it's been for D is the autodecoding done in Phobos, which throws exceptions when it cannot autodecode. The best approach is to simply replace the bad code units with the invalid code point. If any of the other methods you mentioned are desired, that can be implemented by creating a filter which looks for an invalid code point, and then throws or deletes it or whatever the filter is programmed to do.
Jun 29
parent reply Jon Degenhardt <jondegenhardt nowhere.com> writes:
On Monday, 29 June 2026 at 07:55:14 UTC, Walter Bright wrote:
 On 6/28/2026 10:16 PM, Jon Degenhardt wrote:
 Unicode error handling is worth attention because of all the 
 trouble it's been for D.
The trouble it's been for D is the autodecoding done in Phobos, which throws exceptions when it cannot autodecode. The best approach is to simply replace the bad code units with the invalid code point. If any of the other methods you mentioned are desired, that can be implemented by creating a filter which looks for an invalid code point, and then throws or deletes it or whatever the filter is programmed to do.
Autodecoding was a mistake and should be removed in a versioned Phobos library. If only one strategy is going to be supported by library code, the best choice is pass-through, not replacement. That way the application layer can do post filtering and handle the error itself. Replacement is information lossy and cannot be undone or compensated for at the application level. Note that it is common for applications that act as intermediate text processing steps are required to support pass-through behavior. --Jon
Jun 29
parent Walter Bright <newshound2 digitalmars.com> writes:
On 6/29/2026 11:31 PM, Jon Degenhardt wrote:
 If only one strategy is going to be supported by library code, the best choice 
 is pass-through, not replacement. That way the application layer can do post 
 filtering and handle the error itself. Replacement is information lossy and 
 cannot be undone or compensated for at the application level. Note that it is 
 common for applications that act as intermediate text processing steps are 
 required to support pass-through behavior.
The replacement happens only when decoding is required by the algorithm. A text substring search, for example, does not do decoding. It's just an array of bytes.
Jul 03
prev sibling next sibling parent reply libxmoc <libxmoc gmail.com> writes:
AI elevates great architects but empowers unqualified users to 
fill codebases with toxic technical debt.

 From my experience, AI should never be let unsupervised.

I seen that DMD started to merge Claude contributions, and that's 
a problem because most of them are merged by the same person who 
submitted them.

D foundation should double the requirements needed to merge 
contributions made by AI, it should get at least 2 approvals from 
2 different persons, and never be merged by the same person, they 
should also be properly tagged so they can be easily reviewed, 
after the fact, by anyone who wants to help.

Contributors come and go, but the code they leave behind is 
permanent. If we care about the future of DMD, we cannot let 
short term AI convenience compromise long term codebase health.

I have no desire reading Bun's codebase for example, because I 
know none of those concerns are valued, so why should I, the 
human, bother?
May 24
next sibling parent reply libxmoc <libxmoc gmail.com> writes:
Do not get me wrong, I am pro AI, I advocate for automating 
software just like we are automating our factories, but the 
transition has to be handled with extra care!
May 24
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 3:27 AM, libxmoc wrote:
 Do not get me wrong, I am pro AI, I advocate for automating software just like 
 we are automating our factories, but the transition has to be handled with
extra 
 care!
AI is just another tool.
May 24
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 3:25 AM, libxmoc wrote:
 most of them are merged by the same person who submitted them.
That's against our policy.
May 24
prev sibling next sibling parent reply matheus <matheus gmail.com> writes:
On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:
 ...
 I've not vibe coded enough to see it for myself, but I've been 
 told that AI generated code simply generates code until it 
 works. It doesn't do very well at managing complexity. 
 Something to watch out for.
I haven't vibe coding as well, but I have being using it to generate some code sql queries scripts or writing a better ones and it wasn't a pleasant experience. Where I work Devs are using it and some sql queries generated are pretty awful, making our Exadata suffering. Another thing, as DBA the other I asked the AI to generate a script to purge sql profiles or baselines from a given SQL_ID which is a very known script used for DBAs out there, the AI generate a script that would have clear this but for ALL queries on the Database, which could have be a huge mess. After telling the AI the script was wrong and it would have put my job at risk, the AI agreed, asked for excuses and generated a correct version later. xD Anyway I agree sometimes the AI can help a lot mainly for that dumb work, and it's nice for generating test cases too, but still It needed to be supervised. Matheus.
May 24
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 5:08 AM, matheus wrote:
 Anyway I agree sometimes the AI can help a lot mainly for that dumb work, and 
 it's nice for generating test cases too, but still It needed to be supervised.
A wise decision. I wouldn't put my airplane on autopilot so I could go to the lav.
May 24
prev sibling next sibling parent reply Lance Bachmeier <no spam.net> writes:
On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:
 by John Ousterhout
 For example, the title of Chapter 10 is "Define Errors out of 
 Existence". I've been trying to do that since the 1980s.
I haven't read that book, but Ousterhout has made the same point elsewhere for years, and I've never grasped the practical side of his argument. It would be nice to do, but it's a necessary part of a working program.
 I've not vibe coded enough to see it for myself, but I've been 
 told that AI generated code simply generates code until it 
 works. It doesn't do very well at managing complexity. 
 Something to watch out for.
There's a solution to this. I have the LLM program in a "new language" and upload the spec with some examples. No global state. A maximum of 60 lines in the body of a function. A maximum of 10 function arguments. No code blocks. Only goto for jumping around inside the function. You need to change the syntax to get it to accept that you have a new language, so I require line numbers at the start of every line. The problem with just telling the LLM to write a D program that does such and such without restrictions is that it wasn't trained to impose constraints like this to keep the complexity under control, and there's no way it can work well with the amount of context that's required to write a program that can easily be modified in the future.
May 24
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 6:13 AM, Lance Bachmeier wrote:
 I haven't read that book, but Ousterhout has made the same point elsewhere for 
 years, and I've never grasped the practical side of his argument. It would be 
 nice to do, but it's a necessary part of a working program.
You are correct that it is not necessary. However, I recognize a number of complexity-reducing techniques I use that are in the book.
 There's a solution to this. I have the LLM program in a "new language" and 
 upload the spec with some examples. No global state. A maximum of 60 lines in 
 the body of a function. A maximum of 10 function arguments. No code blocks.
Only 
 goto for jumping around inside the function. You need to change the syntax to 
 get it to accept that you have a new language, so I require line numbers at
the 
 start of every line.
 
 The problem with just telling the LLM to write a D program that does such and 
 such without restrictions is that it wasn't trained to impose constraints like 
 this to keep the complexity under control, and there's no way it can work well 
 with the amount of context that's required to write a program that can easily
be 
 modified in the future.
I agree with no global state, however, the 60 line limit is arbitrary. Complexity comes from how much state you need to keep in your head, not how many lines of code.
May 24
parent reply Lance Bachmeier <no spam.net> writes:
On Monday, 25 May 2026 at 05:55:59 UTC, Walter Bright wrote:
 There's a solution to this. I have the LLM program in a "new 
 language" and upload the spec with some examples. No global 
 state. A maximum of 60 lines in the body of a function. A 
 maximum of 10 function arguments. No code blocks. Only goto 
 for jumping around inside the function. You need to change the 
 syntax to get it to accept that you have a new language, so I 
 require line numbers at the start of every line.
 
 The problem with just telling the LLM to write a D program 
 that does such and such without restrictions is that it wasn't 
 trained to impose constraints like this to keep the complexity 
 under control, and there's no way it can work well with the 
 amount of context that's required to write a program that can 
 easily be modified in the future.
I agree with no global state, however, the 60 line limit is arbitrary. Complexity comes from how much state you need to keep in your head, not how many lines of code.
The limit of 60 lines is for the LLM. It hadn't even entered my mind, but was proposed by Gemini. It allows a proper balance between being long enough to incorporate the needed functionality while keeping the necessary context low enough that the output is highly likely to be correct.
May 25
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/25/2026 8:02 AM, Lance Bachmeier wrote:
 The limit of 60 lines is for the LLM. It hadn't even entered my mind, but was 
 proposed by Gemini. It allows a proper balance between being long enough to 
 incorporate the needed functionality while keeping the necessary context low 
 enough that the output is highly likely to be correct.
This is conventional wisdom, but the book argues against it, based in the idea that two functions that are strongly connected are not really an improvement.
May 25
prev sibling next sibling parent reply Dejan Lekic <dejan.lekic gmail.com> writes:
On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:
 by John Ousterhout

 https://www.amazon.com/Philosophy-Software-Design-2nd/dp/173210221X
This book is what quite few well-known AI gurus mentioned lately in their podcasts lately. No wonder why. :D
May 24
parent Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 2:19 PM, Dejan Lekic wrote:
 This book is what quite few well-known AI gurus mentioned lately in their 
 podcasts lately. No wonder why. :D
That's how I found out about it!
May 24
prev sibling next sibling parent reply Meta <jared771 gmail.com> writes:
On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:
 My compiler took a different approach. It would just keep 
 enlarging the string buffer until it ran out of memory. Then 
 there was only one error message - "out of memory" - and the 
 compiler would abort. This applied to all sorts of arbitrary 
 limits the C spec placed on things. There was no need to add 
 error recovery code.
I dunno, I don't really agree with this point of view. If your software isn't doing proper error handling, then I don't think you're really writing serious software; you're doing toy hobby projects. It'd drive me insane trying to use a compiler that randomly dies in the middle of compilation with only an "out of memory" message to tell me what went wrong. Imagine if all software were written this way - it'd be complete and utter hell. That exception handling quote from the book seems ridiculous to me as well. I worked in cybersecurity for over 10 years on multi-million LoC SIEM products, huge multi-team, cloud-based, microservice architecture products, enterprise server software... exception handling was never a major source of complexity in any of them. The three major sources of complexity were: 1. The business logic of the applications. 2. Ensuring the various components were initialized properly, atomically, and in the correct order. 3. The enormous tech debt that comes from people doing ugly hacks to get something implemented as quickly as possible, which years down the line become essentially impossible to remove without rewriting huge swathes of code.
May 24
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 5/24/2026 11:15 PM, Meta wrote:
 I dunno, I don't really agree with this point of view. If your software isn't 
 doing proper error handling, then I don't think you're really writing serious 
 software; you're doing toy hobby projects. It'd drive me insane trying to use
a 
 compiler that randomly dies in the middle of compilation with only an "out of 
 memory" message to tell me what went wrong. Imagine if all software were
written 
 this way - it'd be complete and utter hell.
I never saw it fail in practice. You'd have to write a string that was more than 64Kb in size (for the PC) and more than a gigabyte (for modern computers). I don't think that sort of code would be harboring such a string without the programmer knowing about it.
 That exception handling quote from the book seems ridiculous to me as well. I 
 worked in cybersecurity for over 10 years on multi-million LoC SIEM products, 
 huge multi-team, cloud-based, microservice architecture products, enterprise 
 server software... exception handling was never a major source of complexity
in 
 any of them.
As I am unfamiliar with your code, I can't have an opinion on it!
 The three major sources of complexity were:
 
 1. The business logic of the applications.
 2. Ensuring the various components were initialized properly, atomically, and
in 
 the correct order.
 3. The enormous tech debt that comes from people doing ugly hacks to get 
 something implemented as quickly as possible, which years down the line become 
 essentially impossible to remove without rewriting huge swathes of code.
Yes, of course!
May 25
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
I forgot to mention - an out of memory error is nearly always a fatal error for 
a program. There's no reason one couldn't, when failing to enlarge the string 
buffer:
```
char* buffer = cast(char*)malloc(newsize);
if (!buffer)
     fatal_error("string too big for memory!");
```

And another problem with exceptions are the "double fault" ones, where an 
exception is thrown from within an exception. That's a fatal error.
May 25
parent Meta <jared771 gmail.com> writes:
On Monday, 25 May 2026 at 17:26:59 UTC, Walter Bright wrote:
 I forgot to mention - an out of memory error is nearly always a 
 fatal error for a program. There's no reason one couldn't, when 
 failing to enlarge the string buffer:
 ```
 char* buffer = cast(char*)malloc(newsize);
 if (!buffer)
     fatal_error("string too big for memory!");
 ```
Ah yes, okay, that's at least better than a cryptic "out of memory" error.
 And another problem with exceptions are the "double fault" 
 ones, where an exception is thrown from within an exception. 
 That's a fatal error.
True, although most software I've worked on was written almost entirely in Java, with supplementary pieces on Python, Perl, Go, Lua, Bash, etc. I think these type of "double fault" exceptions are mostly a problem in C++, because the way it does exceptions is so convoluted. Most "managed" languages do it differently, and IMO better. Java for example uses a similar "chaining" mechanism to D, which I've used heavily in a lot of code I've written.
May 25
prev sibling parent Meta <jared771 gmail.com> writes:
On Monday, 25 May 2026 at 16:41:35 UTC, Walter Bright wrote:
 On 5/24/2026 11:15 PM, Meta wrote:
 I dunno, I don't really agree with this point of view. If your 
 software isn't doing proper error handling, then I don't think 
 you're really writing serious software; you're doing toy hobby 
 projects. It'd drive me insane trying to use a compiler that 
 randomly dies in the middle of compilation with only an "out 
 of memory" message to tell me what went wrong. Imagine if all 
 software were written this way - it'd be complete and utter 
 hell.
I never saw it fail in practice. You'd have to write a string that was more than 64Kb in size (for the PC) and more than a gigabyte (for modern computers). I don't think that sort of code would be harboring such a string without the programmer knowing about it.
Okay, fair enough. I can't ever recall seeing strings that large in source code, so it's probably not a case that you'd ever have to worry about, realistically. Still, wouldn't it be better to fail compilation with a "string too long" error message instead of "out of memory"? It's those weird, very niche corner cases where you most want descriptive error messages.
May 25
prev sibling next sibling parent Indraj Gandham <newsgroups indraj.net> writes:
About error handling:

(1) handling all errors increases code complexity;
(2) not all errors which *can* occur *will* occur for the specific 
states in your program; and
(3) not all errors are recoverable in practice, even if they could be in 
theory.

I understand that exception handling might be considered "complex" from 
the compiler-writer's perspective, but from the perspective of 
application logic that simply is not true.

Replacing exception handling with some other mechanism does not 
magically make the issue of stack unwinding go away. You simply have to 
do it manually instead, and often for conditions that you don't care about.

A far more serious and concerning source of complexity is retrofitting 
overly ambitious features onto a design that was never intended for them.

Indraj
May 25
prev sibling next sibling parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Mon, May 25, 2026 at 09:36:03AM +0100, Indraj Gandham via Digitalmars-d
wrote:
[...]
 A far more serious and concerning source of complexity is retrofitting
 overly ambitious features onto a design that was never intended for
 them.
[...] And having to do this by an unreasonably short deadline because some high-paying customer demands for it to have been done by yesterday, resulting in hasty one-time hacks that break good coding practices and accumulate over time to be a huge technical debt that takes years of effort down the road to fix. T -- The past, present, and future walk into a bar. It was tense. Then the physicist walks in, and it was tensor. Finally, the mathematician walks in, and it was ten sets.
May 25
prev sibling next sibling parent Guillaume Piolat <first.name gmail.com> writes:
On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:
 I've not vibe coded enough to see it for myself, but I've been 
 told that AI generated code simply generates code until it 
 works. It doesn't do very well at managing complexity. 
 Something to watch out for.
My own understanding of it at the moment is that there are many shades of "vibey" AI usage: - Code reviews: useful and often find small things, it kinda replaces linters and static analysis. Arguably LLMs are better at reading than writing. Bug finding can feel fresh if you already know it's an uninteresting bug. - Constrained design: "do X using Y method". You tend to keep ownership and control of the design. Though the LLM may have to work more because of the constraints (such as : "use this language"). - Full vibecoding is the most strange, you tend to get a usable output though with objectionnable architecture and "debt". In this mode you don't even look at the code. At times, the LLM will suggest astoundingly bad ideas but mostly it will be quite good. Better when the LLM can assess results itself, lest it's bottleneck by doing QA and visual inspection. Essentially feels like product design and quality assurance. The bad side is that you get 0 ownership feel and no confidence it works beyond the tested cases. The tools are made more dangerous because they encourage to not look at the code, and in organizations you can read a lot of horror stories.
May 25
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:
 For example, the title of Chapter 10 is "Define Errors out of 
 Existence". I've been trying to do that since the 1980s.
Sounds a bit disturbing, on error resume next is a method to define errors out of existence. The replacement character being good for unicode is an exclusive property of unicode.
 My compiler took a different approach. It would just keep 
 enlarging the string buffer until it ran out of memory. Then 
 there was only one error message - "out of memory" - and the 
 compiler would abort.
On linux if a service overallocates, it makes the system unresponsive. I suppose systemd somehow disables oom killer presumably with an intention to let systemd manage resource limits, and setrlimit doesn't work for memory for some reason.
Jun 16
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/16/2026 5:51 AM, Kagamin wrote:
 On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:
 For example, the title of Chapter 10 is "Define Errors out of Existence". I've 
 been trying to do that since the 1980s.
Sounds a bit disturbing, on error resume next is a method to define errors out of existence. The replacement character being good for unicode is an exclusive property of unicode.
It behaves much like the floating point NaN value, which is a generally misunderstood and underappreciated.
Jun 27
parent reply Forum User <forumuser example.com> writes:
On Saturday, 27 June 2026 at 19:48:44 UTC, Walter Bright wrote:
 [...]
 It behaves much like the floating point NaN value, which is a 
 generally misunderstood and underappreciated.
NaNs increase hidden complexity and require more source code (i.e. increase source code complexity) in order to do things right: auto t = (t2); if (t < 2.0) writeln ("met the climate target"); else writeln ("failed to meet the climate target");
Jun 27
next sibling parent Forum User <forumuser example.com> writes:
On Sunday, 28 June 2026 at 00:13:09 UTC, Forum User wrote:
    auto t = (t2);
should have read auto t = sqrt (t2);
Jun 27
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/27/2026 5:13 PM, Forum User wrote:
 On Saturday, 27 June 2026 at 19:48:44 UTC, Walter Bright wrote:
 [...]
 It behaves much like the floating point NaN value, which is a generally 
 misunderstood and underappreciated.
NaNs increase hidden complexity and require more source code (i.e. increase source code complexity) in order to do things right:    auto t = (t2);    if (t < 2.0)       writeln ("met the climate target");    else       writeln ("failed to meet the climate target");
The reader would want to know how well the number met the target, or how badly it failed: ```d if (t < 2.0) writeln ("%g met the climate target", t); else writeln ("%g failed to meet the climate target", t); ``` Besides, getting a NaN result is always better than getting an arbitrary number with no clue whether it is valid or a bug.
Jun 27
next sibling parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Sat, Jun 27, 2026 at 07:15:37PM -0700, Walter Bright via Digitalmars-d wrote:
 On 6/27/2026 5:13 PM, Forum User wrote:
 On Saturday, 27 June 2026 at 19:48:44 UTC, Walter Bright wrote:
 [...]
 It behaves much like the floating point NaN value, which is a
 generally misunderstood and underappreciated.
NaNs increase hidden complexity and require more source code (i.e. increase source code complexity) in order to do things right: auto t = (t2); if (t < 2.0) writeln ("met the climate target"); else writeln ("failed to meet the climate target");
The reader would want to know how well the number met the target, or how badly it failed: ```d if (t < 2.0) writeln ("%g met the climate target", t); else writeln ("%g failed to meet the climate target", t); ``` Besides, getting a NaN result is always better than getting an arbitrary number with no clue whether it is valid or a bug.
In an exception-based system you'd get an exception, not an arbitrary number. Same difference. Besides, even if you *don't* get a NaN, it doesn't mean there wasn't a bug. The calculation itself can still be faulty, and you still have the same problem of how much to trust the result. T -- Those who don't understand D are condemned to reinvent it, poorly. -- Daniel N
Jun 27
prev sibling parent reply Forum User <forumuser example.com> writes:
On Sunday, 28 June 2026 at 02:15:37 UTC, Walter Bright wrote:
 On 6/27/2026 5:13 PM, Forum User wrote:
 On Saturday, 27 June 2026 at 19:48:44 UTC, Walter Bright wrote:
 [...]
 It behaves much like the floating point NaN value, which is a 
 generally misunderstood and underappreciated.
NaNs increase hidden complexity and require more source code (i.e. increase source code complexity) in order to do things right:    auto t = (t2);    if (t < 2.0)       writeln ("met the climate target");    else       writeln ("failed to meet the climate target");
The reader would want to know how well the number met the target, or how badly it failed:
Actually the NaN-aware code must read like this: ```d if (t != t) { writeln ("a NaN occured"); // stop execution, return error code } else if (t < 2.0) writeln ("%g met the climate target", t); else writeln ("%g failed to meet the climate target", t); ``` I neither want to write nor read such code. But the usual case is that the (contaminated) value is not intended to be presented to the reader of the program output. Every internal double variable is subject to such contamination. Lots of numerical algorithms contain branches based on then invalid comparisions and there is no NaN-propagation across such branches. The result is: The formerly detected error is swept under the carpet. THIS is a totally underappreciated source of errors.
 [...]
 Besides, getting a NaN result is always better than getting an 
 arbitrary number with no clue whether it is valid or a bug.
There is no number printed in the invalid case in any of the snippets presented so far.
Jun 28
next sibling parent reply Kapendev <alexandroskapretsos gmail.com> writes:
On Sunday, 28 June 2026 at 07:35:45 UTC, Forum User wrote:
 Actually the NaN-aware code must read like this:

 ```d
 if (t != t) {
     writeln ("a NaN occured");
     // stop execution, return error code
 }
 else if (t < 2.0)
     writeln ("%g met the climate target", t);
 else
     writeln ("%g failed to meet the climate target", t);
 ```

 I neither want to write nor read such code.

 But the usual case is that the (contaminated) value is not 
 intended
 to be presented to the reader of the program output. Every 
 internal
 double variable is subject to such contamination. Lots of 
 numerical
 algorithms contain branches based on then invalid comparisions 
 and
 there is no NaN-propagation across such branches. The result 
 is: The
 formerly detected error is swept under the carpet.

 THIS is a totally underappreciated source of errors.
You can misuse any feature, even good ones like NaN. It's a number type. You usually want to continue executing instead of crashing when doing math. "Garbage data" in this specific case is good and an OK way to handle errors. If it was another kind of type, then maybe it makes sense to have a different way to handle errors. -- *A diverse mix of error types is very good for your health.* -- *Diogenes of Sinope*
Jun 28
parent reply Forum User <forumuser example.com> writes:
On Sunday, 28 June 2026 at 10:12:17 UTC, Kapendev wrote:
 You can misuse any feature, even good ones like NaN.
The question is: Does that feature support your work or does it complicate it?
 It's a number type.
NaNs are values, not types.
 You usually want to continue executing instead of crashing when 
 doing math.
That depends on what kind of program you have. If you write software for the navigation of military drones it is usually not necessary to also crash the program. If you write a program which influences political decision making you should favor crashing over giving poor advise.
 "Garbage data" in this specific case is good and an OK way to 
 handle errors.
Garbage data is a sign of a numerical error which is by definition not "good". And having garbage data lingering around for later programmatical examination is the very last thing you want.
 If it was another kind of type,
Uh, now it is not a type?
Jun 28
next sibling parent Kapendev <alexandroskapretsos gmail.com> writes:
On Sunday, 28 June 2026 at 15:50:04 UTC, Forum User wrote:
 On Sunday, 28 June 2026 at 10:12:17 UTC, Kapendev wrote:
 You can misuse any feature, even good ones like NaN.
The question is: Does that feature support your work or does it complicate it?
 It's a number type.
NaNs are values, not types.
 You usually want to continue executing instead of crashing 
 when doing math.
That depends on what kind of program you have. If you write software for the navigation of military drones it is usually not necessary to also crash the program. If you write a program which influences political decision making you should favor crashing over giving poor advise.
 "Garbage data" in this specific case is good and an OK way to 
 handle errors.
Garbage data is a sign of a numerical error which is by definition not "good". And having garbage data lingering around for later programmatical examination is the very last thing you want.
 If it was another kind of type,
Uh, now it is not a type?
I'm saying that the NaN value makes sense for floats, a number type. Not that NaN is a type. Also, it's not the garbage that's good, but the signal it sends that something is wrong in a specific code path. NaN is not designed to handle everything. If you are dealing with a problem that requires specific constraints and you have tested that you actually need that, then wrapping a number type with a struct is the solution. D makes that pretty easy too.
Jun 28
prev sibling parent reply FinalEvilution <FinalEvilution gmail.com> writes:
On Sunday, 28 June 2026 at 15:50:04 UTC, Forum User wrote:

 If you write a program which influences political decision 
 making you should favor crashing over giving poor advise.
I'd just like to note that there are floating point hardware exceptions [FloatingPointControl](https://dlang.org/phobos/std_math_hardware.html#.FloatingPointControl) that can be used. With caveats. No stacktrace, just a coredump. Gotta use gdb to get it. No crash when using an uninitialized variable even tho the docs say it should. Bug? "Note in particular that if invalidException is enabled, a hardware trap will be generated whenever an uninitialized floating-point variable is used." ``` import std.stdio; import std.math.hardware; void main() { FloatingPointControl fpctrl; fpctrl.enableExceptions(FloatingPointControl.allExceptions); writeln(foo(0)); } float foo(float input) { float nan; return 1/input; //return nan/input; // No floating point exception. Bug? } ```
Jun 28
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/28/2026 12:31 PM, FinalEvilution wrote:
 I'd just like to note that there are floating point hardware exceptions 
True, but pretty much nobody enables that.
Jun 29
next sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 30/06/2026 7:54 AM, Walter Bright wrote:
 On 6/28/2026 12:31 PM, FinalEvilution wrote:
 I'd just like to note that there are floating point hardware exceptions 
True, but pretty much nobody enables that.
About that... I've been thinking about a check specifically for this. Initially I thought about a read barrier, but that would be too costly. If however, there was a flag that was set in the ISA that we could read, then we could inject a check at the end of any expression (according to statements), if they contain a float. ```d float n; if (n * 2 > 0) // auto _tmp = n * 2 > 0, !isnanflagset || _d_nanfoundcheck, _tmp ```
Jun 29
prev sibling parent Forum User <forumuser example.com> writes:
On Monday, 29 June 2026 at 19:54:10 UTC, Walter Bright wrote:
 On 6/28/2026 12:31 PM, FinalEvilution wrote:
 I'd just like to note that there are floating point hardware 
 exceptions
True, but pretty much nobody enables that.
Maybe or maybe not [1]. But what was was the argument here? Actually a) these hardware traps are present in all modern processors in the market, b) Traping overflow or invalid operation costs nothing (no additional cycles). If the job cannot "recover" it's the best to let it crash. [1] https://docs.oracle.com/cd/E19957-01/805-4940/z40009062fdd/index.html "The default with f90 is to automatically trap on division by zero, overflow, and invalid operation."
Jun 30
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
You're worse off with a bad number taking a merry path through the
conditionals, 
as you won't know which number is bad.
Jun 28
parent reply Forum User <forumuser example.com> writes:
On Sunday, 28 June 2026 at 23:58:07 UTC, Walter Bright wrote:
 You're worse off with a bad number taking a merry path through 
 the conditionals, as you won't know which number is bad.
Exactly. Enabling hardware traps for invalid op. etc. "Define[s] Errors Out Of Existence".
Jun 30
parent reply Mike Shah <mshah.475 gmail.com> writes:
On Tuesday, 30 June 2026 at 18:48:32 UTC, Forum User wrote:
 On Sunday, 28 June 2026 at 23:58:07 UTC, Walter Bright wrote:
 You're worse off with a bad number taking a merry path through 
 the conditionals, as you won't know which number is bad.
Exactly. Enabling hardware traps for invalid op. etc. "Define[s] Errors Out Of Existence".
NaN as the default(.init) for floats took me a a bit to get use to when learning D -- but personally I like it as a default. When I wrote my first math library for a game, it helped me catch a few bugs (mostly with things like initializing temporary vectors/matrices for calculations)! Probably most importantly, it makes me think about what value to initialize a float to which is important.
Jun 30
parent Walter Bright <newshound2 digitalmars.com> writes:
On 6/30/2026 6:10 PM, Mike Shah wrote:
 Probably most importantly, it 
 makes me think about what value to initialize a float to which is important.
Yes. Many obscure bugs happen because the programmer, who is maintaining the code, does not know what value to initialize it to so just inserts "0" and turns a compile time error into a coding error.
Jul 03
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
If you're following my PRs, I've been working on implementing some of the 
principles in the book to reduce the cognitive complexity of the code generator.

https://github.com/dlang/dmd/pulls?q=is%3Apr+author%3A%40me+is%3Aclosed
Jun 28
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 28/06/2026 7:41 PM, Walter Bright wrote:
 If you're following my PRs, I've been working on implementing some of 
 the principles in the book to reduce the cognitive complexity of the 
 code generator.
 
 https://github.com/dlang/dmd/pulls?q=is%3Apr+author%3A%40me+is%3Aclosed
I especially like the following PR's: "Implement section attribute", "Implement __ctfe attribute", "Implement for fast DFA engine, value tracking and uninitialized memory preventing of reading.". Here is the fixed link: https://github.com/dlang/dmd/pulls?q=is%3Apr+author%3A%40walterbright+is%3Aclosed+
Jun 28
parent Walter Bright <newshound2 digitalmars.com> writes:
On 6/28/2026 12:44 AM, Richard (Rikki) Andrew Cattermole wrote:
 On 28/06/2026 7:41 PM, Walter Bright wrote:
 If you're following my PRs, I've been working on implementing some of the 
 principles in the book to reduce the cognitive complexity of the code
generator.

 https://github.com/dlang/dmd/pulls?q=is%3Apr+author%3A%40me+is%3Aclosed
I especially like the following PR's: "Implement section attribute", "Implement __ctfe attribute", "Implement for fast DFA engine, value tracking and uninitialized memory preventing of reading.". Here is the fixed link: https://github.com/dlang/dmd/pulls?q=is%3Apr+author%3A%40walterbright+is%3Aclosed+
Thank you for the correction. I didn't realize the "me" think in the URL.
Jun 28
prev sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Sunday, 28 June 2026 at 07:41:38 UTC, Walter Bright wrote:
 https://github.com/dlang/dmd/pulls?q=is%3Apr+author%3A%40me+is%3Aclosed
you should checkout my website localhost:8000
Jun 28
prev sibling parent Forum User <forumuser example.com> writes:
On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:
 For example, the title of Chapter 10 is "Define Errors out of 
 Existence".
I used Claude Haiku 4.5 for a confirmation that this chapter covers the design of an unset function. There is also a talk of Outsterhout on YT which covers this topic. [0] This completely made up fragment of code unset ("x"); unset ("x"); may be a copy-paste mistake (modify is missing). The author proba- bly wanted to write unset ("x"); unset ("y"); If the unset is not - as endorsed by Outsterhout - made idempotent but throwing this error would have been detected as early as when the first `make test` is issued. [0] A Philosophy of Software Design | John Ousterhout | Talks at Google https://www.youtube.com/watch?v=bmSAYlu0NcY starting at 00:21:58
Jun 28