digitalmars.D - A Philosophy of Software Design
- Walter Bright (24/24) May 23 by John Ousterhout
- Walter Bright (5/5) May 23 The first sentence of the chapter "Define Errors Out Of Existence" says:
- Richard (Rikki) Andrew Cattermole (11/19) May 23 I've been attempting to find a way to do that for 3+ years.
- Walter Bright (19/19) May 23 Thanks. This is an interesting general purpose method.
- Richard (Rikki) Andrew Cattermole (18/28) May 23 Where possible yes, you should remove the possibility of needing to
- Richard (Rikki) Andrew Cattermole (11/44) May 23 Code Complete 2nd edition has three chapters about error handling,
- Richard (Rikki) Andrew Cattermole (23/71) May 23 "If you have the luxury of designing an API, design it in such a way
- Walter Bright (20/20) May 23 Microsoft is not wrong. You're not wrong, either.
- Richard (Rikki) Andrew Cattermole (48/73) May 23 My PoV is that you gotta use the right tool for the job.
- Walter Bright (13/37) May 24 See the book I suggested, "A Philosophy of Software Design", which shows...
- Walter Bright (1/1) May 23 BTW, I ordered the 3 books. Thanks!
- Richard (Rikki) Andrew Cattermole (8/11) May 23 That's not actually how you handled it, although I'm sure it solved some...
- Basile B. (16/28) May 24 I think a better example is when in expsema the type of an
- Walter Bright (5/13) May 26 What the Error nodes did was eliminate the cascade of error messages res...
- Paul Backus (19/34) May 24 This is one way to define exceptions out of existence: expand the
- Walter Bright (8/24) May 24 The idea is to reduce complexity by not having separate error paths entw...
- H. S. Teoh (31/41) May 24 You convinced me that macros are a terrible idea. :-D Actually, you
- Dom Disc (8/11) May 25 This is why I always suggested a NaN value for all types. For
- Walter Bright (3/9) May 25 Unfortunately, since integer arithmetic may use T.min, it's not going to...
- Basile B. (4/18) May 26 Bollocks. Here you are mistalind software errors with how a
- H. S. Teoh (9/18) May 24 On the contrary, you yourself said back in the day that exception
- Walter Bright (20/25) May 24 Yes, I did say that. But as I also wrote, my thinking has evolved over t...
- H. S. Teoh (60/94) May 24 IMO, the problems stem from conflating these two distinct operations
- Walter Bright (12/22) May 24 You do have a point. However, it's much easier to put a single error che...
- H. S. Teoh (29/40) May 25 The current implementation of writefln leaves a lot of room for
- Richard (Rikki) Andrew Cattermole (5/51) May 25 There is a simple way to archive this, use IES.
- H. S. Teoh (7/19) May 25 [...]
- Richard (Rikki) Andrew Cattermole (2/23) May 25 https://dlang.org/spec/istring.html
- Dejan Lekic (4/8) May 25 100%
- Zz (4/12) May 24 Might find this interesting.
- Kapendev (14/16) May 24 The error to read a file in Odin:
- Walter Bright (3/4) May 24 "OOOOdddiiiiinnnn!!!!"
- Basile B. (6/20) May 28 That's a good challenge, really. The problem I have with that
- H. S. Teoh (20/29) May 28 [...]
- tony (6/10) Jun 27 Ignoring errors definitely reduces complexity, and seems to be
- monkyyy (5/17) Jun 27 Its still a bad goto with a "comefrom" statement, ideally d
- H. S. Teoh (40/52) Jun 27 The correct understanding is to reduce complexity by rephrasing your
- Walter Bright (29/54) Jun 27 The choice is:
- H. S. Teoh (54/107) Jun 27 One could also argue that it's preferable for the error path to be
- Walter Bright (8/8) Jun 27 Yesterday, I watched an episode of "Aviation Disasters". In it, the airp...
- Forum User (5/16) Jun 28 Air France 447? They were not broken but some were clogged and
- Walter Bright (8/12) Jun 29 It was Birgenair Flight 301. The investigators guessed that the pitot tu...
- Walter Bright (11/51) Jun 27 If it's a fatal error, that's ok. But if you need to "fix and continue",...
- Alexandru Ermicioi (3/19) Jun 28 You'd typically pass this info in exception itself, therefore
- Walter Bright (7/9) Jun 29 There wouldn't be an exception thrown if:
- Richard (Rikki) Andrew Cattermole (10/22) Jun 29 If it propergates yes, you need a read barrier check and throw if NaN.
- Walter Bright (3/12) Jun 29 Default initializing f to nan catches it and propagates the error. Defau...
- Richard (Rikki) Andrew Cattermole (34/48) Jun 29 At _runtime_ yes.
- Walter Bright (8/8) Jul 03 Relying on DFA to catch null and nan values will only catch a very small...
- H. S. Teoh (14/56) Jun 28 Sure it does. The stacktrace in e tells you exactly which one failed.
- Walter Bright (3/4) Jun 29 Yes, it can. The stdout can store that information and then carry on. At...
- Richard (Rikki) Andrew Cattermole (112/134) Jun 27 The first.
- Jon Degenhardt (37/45) Jun 28 A old paper relevant to error handling is [END-TO-END ARGUMENTS
- Walter Bright (7/9) Jun 29 The trouble it's been for D is the autodecoding done in Phobos, which th...
- Jon Degenhardt (11/21) Jun 29 Autodecoding was a mistake and should be removed in a versioned
- Walter Bright (3/9) Jul 03 The replacement happens only when decoding is required by the algorithm....
- libxmoc (17/17) May 24 AI elevates great architects but empowers unqualified users to
- libxmoc (3/3) May 24 Do not get me wrong, I am pro AI, I advocate for automating
- Walter Bright (2/5) May 24 AI is just another tool.
- Walter Bright (2/3) May 24 That's against our policy.
- matheus (18/23) May 24 I haven't vibe coding as well, but I have being using it to
- Walter Bright (2/4) May 24 A wise decision. I wouldn't put my airplane on autopilot so I could go t...
- Lance Bachmeier (18/25) May 24 I haven't read that book, but Ousterhout has made the same point
- Walter Bright (6/21) May 24 You are correct that it is not necessary. However, I recognize a number ...
- Lance Bachmeier (6/23) May 25 The limit of 60 lines is for the LLM. It hadn't even entered my
- Walter Bright (3/7) May 25 This is conventional wisdom, but the book argues against it, based in th...
- Dejan Lekic (3/5) May 24 This book is what quite few well-known AI gurus mentioned lately
- Walter Bright (2/4) May 24 That's how I found out about it!
- Meta (21/27) May 24 I dunno, I don't really agree with this point of view. If your
- Walter Bright (7/26) May 25 I never saw it fail in practice. You'd have to write a string that was m...
- Walter Bright (10/10) May 25 I forgot to mention - an out of memory error is nearly always a fatal er...
- Meta (10/21) May 25 Ah yes, okay, that's at least better than a cryptic "out of
- Meta (7/21) May 25 Okay, fair enough. I can't ever recall seeing strings that large
- Indraj Gandham (15/15) May 25 About error handling:
- H. S. Teoh (13/16) May 25 [...]
- Guillaume Piolat (23/27) May 25 My own understanding of it at the moment is that there are many
- Kagamin (8/14) Jun 16 Sounds a bit disturbing, on error resume next is a method to
- Walter Bright (3/10) Jun 27 It behaves much like the floating point NaN value, which is a generally
- Forum User (9/12) Jun 27 NaNs increase hidden complexity and require more source code
- Forum User (3/4) Jun 27 should have read
- Walter Bright (11/24) Jun 27 The reader would want to know how well the number met the target, or how...
- H. S. Teoh (9/36) Jun 27 In an exception-based system you'd get an exception, not an arbitrary
- Forum User (26/46) Jun 28 Actually the NaN-aware code must read like this:
- Kapendev (9/33) Jun 28 You can misuse any feature, even good ones like NaN.
- Forum User (14/21) Jun 28 The question is: Does that feature support your work or does it
- Kapendev (9/30) Jun 28 I'm saying that the NaN value makes sense for floats, a number
- FinalEvilution (26/28) Jun 28 I'd just like to note that there are floating point hardware
- Walter Bright (2/3) Jun 29 True, but pretty much nobody enables that.
- Richard (Rikki) Andrew Cattermole (12/16) Jun 29 About that...
- Forum User (14/18) Jun 30 Maybe or maybe not [1]. But what was was the argument here?
- Walter Bright (2/2) Jun 28 You're worse off with a bad number taking a merry path through the condi...
- Forum User (3/5) Jun 30 Exactly. Enabling hardware traps for invalid op. etc. "Define[s]
- Mike Shah (8/13) Jun 30 NaN as the default(.init) for floats took me a a bit to get use
- Walter Bright (4/6) Jul 03 Yes. Many obscure bugs happen because the programmer, who is maintaining...
- Walter Bright (3/3) Jun 28 If you're following my PRs, I've been working on implementing some of th...
- Richard (Rikki) Andrew Cattermole (6/11) Jun 28 I especially like the following PR's: "Implement section attribute",
- Walter Bright (2/14) Jun 28 Thank you for the correction. I didn't realize the "me" think in the URL...
- monkyyy (2/3) Jun 28 you should checkout my website localhost:8000
- Forum User (20/22) Jun 28 I used Claude Haiku 4.5 for a confirmation that this chapter
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
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
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
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
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
On 24/05/2026 4:27 PM, Richard (Rikki) Andrew Cattermole wrote:On 24/05/2026 4:10 PM, Walter Bright wrote: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.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
On 24/05/2026 5:02 PM, Richard (Rikki) Andrew Cattermole wrote:On 24/05/2026 4:27 PM, Richard (Rikki) Andrew Cattermole wrote:"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.On 24/05/2026 4:10 PM, Walter Bright wrote: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.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
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
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
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.It was the wrong decision. Even Andrei came around on this, to his great credit.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.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
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
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: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).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 24
On 5/23/2026 11:49 PM, Richard (Rikki) Andrew Cattermole wrote:On 24/05/2026 4:10 PM, Walter Bright wrote: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.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.
May 26
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
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
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
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
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
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
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
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
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: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: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.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
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
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:[...]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.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.Let's hope it will not be another over-engineered solution. T -- Every time you make a typo, the errorists win.
May 25
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: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...On 5/24/2026 5:47 PM, 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: ``` 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.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.Let's hope it will not be another over-engineered solution.
May 25
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
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:https://dlang.org/spec/istring.htmlOn 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
May 25
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
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
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
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
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: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.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 28
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:[...][...] 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. -- Crivenshttps://rm4n0s.github.io/posts/3-error-handling-challenge/ ZzThat'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
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
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: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 cThe 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
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 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?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?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
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
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:[...]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.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.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.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.)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
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
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
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
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
On Sunday, 28 June 2026 at 04:05:34 UTC, Walter Bright wrote:You'd typically pass this info in exception itself, therefore getting file and number, and runtime values causing this.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.
Jun 28
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
On 29/06/2026 7:58 PM, Walter Bright wrote:On 6/28/2026 5:14 AM, Alexandru Ermicioi wrote: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; } ```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
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
On 30/06/2026 7:49 AM, Walter Bright wrote:On 6/29/2026 1:05 AM, Richard (Rikki) Andrew Cattermole wrote:I just noticed that is void and should be float oops.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.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
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
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:I thought that's what unittest blocks were for? ;)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.Sure it does. The stacktrace in e tells you exactly which one failed. [...]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 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.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 28
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
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
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
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
On Monday, 29 June 2026 at 07:55:14 UTC, Walter Bright wrote:On 6/28/2026 10:16 PM, Jon Degenhardt wrote: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. --JonUnicode 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
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
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
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
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
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
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
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
On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:by John OusterhoutFor 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
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
On Monday, 25 May 2026 at 05:55:59 UTC, Walter Bright 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.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 25
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
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/173210221XThis book is what quite few well-known AI gurus mentioned lately in their podcasts lately. No wonder why. :D
May 24
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. :DThat's how I found out about it!
May 24
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
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
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
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
On Monday, 25 May 2026 at 16:41:35 UTC, Walter Bright wrote:On 5/24/2026 11:15 PM, Meta wrote: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.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.
May 25
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
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
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
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
On 6/16/2026 5:51 AM, Kagamin wrote:On Sunday, 24 May 2026 at 01:39:48 UTC, Walter Bright wrote:It behaves much like the floating point NaN value, which is a generally misunderstood and underappreciated.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.
Jun 27
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
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
On 6/27/2026 5:13 PM, Forum User wrote:On Saturday, 27 June 2026 at 19:48:44 UTC, Walter Bright wrote: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.[...] 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
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: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 NOn Saturday, 27 June 2026 at 19:48:44 UTC, Walter Bright wrote: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.[...] 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
On Sunday, 28 June 2026 at 02:15:37 UTC, Walter Bright wrote:On 6/27/2026 5:13 PM, 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.On Saturday, 27 June 2026 at 19:48:44 UTC, Walter Bright wrote:The reader would want to know how well the number met the target, or how badly it failed:[...] 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");[...] 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
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
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
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: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.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
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
On 6/28/2026 12:31 PM, FinalEvilution wrote:I'd just like to note that there are floating point hardware exceptionsTrue, but pretty much nobody enables that.
Jun 29
On 30/06/2026 7:54 AM, Walter Bright wrote:On 6/28/2026 12:31 PM, FinalEvilution wrote: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 ```I'd just like to note that there are floating point hardware exceptionsTrue, but pretty much nobody enables that.
Jun 29
On Monday, 29 June 2026 at 19:54:10 UTC, Walter Bright wrote:On 6/28/2026 12:31 PM, FinalEvilution wrote: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."I'd just like to note that there are floating point hardware exceptionsTrue, but pretty much nobody enables that.
Jun 30
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
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
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: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.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
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
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
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%3AclosedI 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
On 6/28/2026 12:44 AM, Richard (Rikki) Andrew Cattermole wrote:On 28/06/2026 7:41 PM, Walter Bright wrote:Thank you for the correction. I didn't realize the "me" think in the URL.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%3AclosedI 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
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%3Aclosedyou should checkout my website localhost:8000
Jun 28
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









Walter Bright <newshound2 digitalmars.com> 