News for hackers. Stuff that matters.
Re:1/0 = 0 by WalterBright on Friday August 10, 2018 @11:07PM (#17738056)
An issue with D that has repeatedly engendered heated debate is what should happen when a programming bug is detected at runtime. The two camps are:

1. The program should "soldier on" if it can.

2. The program should go immediately to jail, it must not pass Go, and must not collect $200.

I'm solidly in the latter camp. If a program has entered a state unanticipated by the programmer, then there is no way to know how it entered that state (until it is debugged), and hence no way to know what the program might do next (such as load malware).

1/0 is a bug, and the program should immediately halt.

Re:1/0 = 0 by Ace17 on Saturday August 11, 2018 @04:03AM (#17738833)
> If a program has entered a state unanticipated by the programmer, then there is no way to know how it entered that state [...] and hence no way to know what the program might do next (such as load malware).

... or kill the patient ( https://en.wikipedia.org/wiki/Therac-25 ) or set the company bankrupt overnight ( https://en.wikipedia.org/wiki/Knight_Capital_Group ), or simply delete all your data (happened to me with some buggy media player that though my $HOME directory belonged to its download cache).

Programming languages don't generally allow us to "handle", from inside the faulty process, stuff like NULL-pointer dereference, double-free, or segmentation faults. And that's a good thing, because as you said, at this point, there's a very high probability the program is in some rogue state.

(Making special cases for NullPointerException or OutOfBoundsException, like some languages do, is, IMHO, a bad idea, that spreads the confusion between programming mistakes (i.e coming from the source code) and invalid runtime conditions (coming from environment). (I'm avoiding the ambiguous terms "errors" or "bugs" here)).

Making "divide by zero" non-fatal belongs to the same family than making "((int)0)" return zero, or making "((int)0) = x" a no-op. At first, it seems like this will only hide programming mistakes ; but this impression comes from my current programming habits, which are tailored to avoid these situations.

But maybe there are advantages in being able to write these things on purpose (at the cost of losing the runtime detection of some programming mistakes). After all, I'm perfectly happy with "free(NULL)".

Re:1/0 = 0 by Boulth on Saturday August 11, 2018 @10:53AM (#17739878)
> (Making special cases for NullPointerException or OutOfBoundsException, like some languages do, is, IMHO, a bad idea, that spreads the confusion between programming mistakes (i.e coming from the source code) and invalid runtime conditions (coming from environment). (I'm avoiding the ambiguous terms "errors" or "bugs" here)).

Actually if you check out the interviews with Java designers the checked exceptions mechanism was designed for that purpose: unchecked exceptions are for bugs that should never happen in a well written code while checked exceptions are for conditions that can happen regardless to how good is your code (e.g. i/o errors).

See: https://www.artima.com/intv/solid.html

There are of course also Errors for fatal conditions that should been be caught (actually in CLR even if you catch the exceptions will be re-raised automatically at the end of the handler).

Another interesting thing in CLR is Constrained Execution Regions that allow you to run the cleanup code reliably even when such a fatal condition is encountered (but the code to be run is limited e.g. it cannot allocate).

Re:1/0 = 0 by karmakaze on Saturday August 11, 2018 @02:13PM (#17740825)
The mis-categorization of some exceptions[0] as checked or not is part of why checked exceptions seem like a mistake. The other is when exceptions cross boundaries of human organizations like nested libraries.

[0] NumberFormatException is a RuntimeError as are all IllegalArgumentException, WTF?

Re:1/0 = 0 by dllthomas on Monday August 13, 2018 @11:37AM (#17750885)
Another is that well-known attempts at checked exceptions in practice provide an incredibly limited vocabulary for talking about raised exceptions - usually just unconditional "might raise X" or "does not raise X". If you could say things like "raises anything raised by my argument f, except for X because I catch that" it seems like it might be a much better idea (especially if you can infer those...)
Re:1/0 = 0 by sacado2 on Saturday August 11, 2018 @11:19AM (#17739975)
> (Making special cases for NullPointerException or OutOfBoundsException, like some languages do, is, IMHO, a bad idea, that spreads the confusion between programming mistakes (i.e coming from the source code) and invalid runtime conditions (coming from environment). (I'm avoiding the ambiguous terms "errors" or "bugs" here)).

And that's why I like dual error mechanisms, like the panic vs manual error handling mechanism in Go (despite all its verbosity). It's very important to make the difference between an external error (that should be considered something "normal" to deal with) and an invalid state (that should lead to a halt of the program, maybe after logging something, or before trying to restart the program from zero).

Traditional expection mechanisms (à la try/catch) make it too hard to deal with an external error, and too easy to think you can recover from an invalid state.

Re:1/0 = 0 by WalterBright on Saturday August 11, 2018 @04:38PM (#17741387)
D draws a clear distinction between programming bugs (not recoverable) and environmental errors (which can be recoverable).

Failures of the former throw an Error, the latter Exception.

Re:1/0 = 0 by skrebbel on Saturday August 11, 2018 @01:47AM (#17738556)
Great analysis! I think that summarizes a lot of dev arguments. That said, Erlang appears to agree with both camps. If an error occurs, the _process_ immediately crashes (not OS process, it's Erlang Kingo for an actor or a green thread), but the _program_ soldiers on as if nothing happened. The idea behind the design, I believe, is that programmer errors are unavoidable, but that eg a bug in some edge case triggered by uncommon data shouldn't crash the entire server.

I don't know much about Pony, but given that it is also actor based, I'd imagine this would be a great opportunity to crash early and eagerly. It gives you all the advantages of catching a bug early while not crippling a production app under what might be a pretty low-impact, low-frequency bug.

Re:1/0 = 0 by electrum on Saturday August 11, 2018 @02:09AM (#17738599)
Java has essentially the same behavior with threads. If an error occurs in a thread, it will throw an exception that will terminate the thread if uncaught. Other threads are not directly affected. This is typically what you want as threads are usually independent of each other, often processing an isolated request, which may encounter a bug due to bad data or other conditions. Contrast this to languages like C++, where the usual behavior is to segfault and crash the process.

Special care must be taken if threads are manipulating shared data structures, especially inside a "critical section" where the data structure invariants must be maintained. This is why automatically terminating the JVM when OutOfMemoryError occurs is a best practice, as code is often not paranoid enough to handle it.

Re:1/0 = 0 by innocentoldguy on Saturday August 11, 2018 @03:09AM (#17738702)
One big difference between Erlang and Java is that all processes (threads) in Erlang are safely contained in private memory, whereas in Java threads occur in public memory. Another big difference is that Erlang has supervisor processes to restart child processes that have crashed. Java just farts itself.
Re:1/0 = 0 by lomnakkus on Saturday August 11, 2018 @10:18AM (#17739758)
Indeed. Java is in fact a sort of worst-of-all-worlds in that it's even possible to catch things that are really unrecoverable errors like StackOverflowException or OutOfMemoryError. To to uninitiated it might not like appear that catching these is that bad, right? In addition to the problems with finally clauses not necessarily running to completion under such circumstances catching these exceptions can cause extremely hard-to-debug deadlocks because object locks won't be released properly.

This is an area where the design of Java/JVM is badly screwed up.

[1] JVM, really. It's pretty unavoidable on most (all?) JVM languages.

Re:1/0 = 0 by blub on Saturday August 11, 2018 @01:16PM (#17740555)
In C++ the default behaviour is for the runtime to call std::terminate, not segfault. It's easy to catch exceptions by wrapping the callable in a packaged_task and then the exception is available in the future. The key point is that the exception is rethrown when accessing the result through the future.

Swallowing exceptions is a really poor idea. Does Java really do that?

Re:1/0 = 0 by rzzzt on Saturday August 11, 2018 @03:13AM (#17738713)
Erlang originates from telecom, where the behavior you describe makes perfect sense. Equipment should work all the time; you don't want to (or even can't) send out people to debug/restart them.
Re:1/0 = 0 by dnautics on Saturday August 11, 2018 @12:20PM (#17740268)
It's not just programmer error, it can soldier on through some hardware errors, rogue cosmic rays, and arguably the hardest - other people's software errors, like os errors, driver errors, library errors, network stack errors...
Re:1/0 = 0 by btschaegg on Saturday August 11, 2018 @04:41PM (#17741403)
Regarding "some hardware errors": This might include operationg conditions you didn't consider; say:

The divisor in your code went through a non-ECC DRAM module (maybe in a disk drive) that runs hotter than its specified operating temperature would allow and a random bit-flip changes your `1` into a `0`.

On this topic, the talk on Bitsquatting from Artem Dinaburg during Defcon 19 is worth watching. The most interesting part on bit flips starts around 15:05 minutes: https://youtu.be/9WcHsT97suU?t=15m5s

Re:1/0 = 0 by kragen on Tuesday August 14, 2018 @12:17PM (#17759247)
Adopting this policy was what destroyed the first Ariane 5.[0] IIRC, a conversion from floating-point to a 16-bit integer overflowed, raising an exception which reset the flight control system. The flight control system was redundant, but both copies experienced the same error. The result that was being computed would not have been used.

The lesson I took from this is that neither of these two options is acceptable in software that has to work all the time, like much real-time software. Instead, we should detect all the bugs in such software before runtime, which is only feasible for small systems such as seL4[1]. So we should diligently minimize this software.

For other programs, the best way to handle an error is context-dependent. If a rendering bug will result in a glitch showing in a texture onscreen, that's preferable to exiting the game in the middle of a raid. If a PID motor control system for a robot arm has a division by zero, halting the program without first slowing the motors to a stop could be catastrophic, even causing fatalities. And of course there are numerous cases where continuing execution in the face of such an error is far worse.

[0]: https://web.archive.org/web/20000815230639/http://www.esrin.... [1]: http://sel4.systems/

Re:1/0 = 0 by karmakaze on Saturday August 11, 2018 @02:06PM (#17740787)
To say that either (1) or (2) is always the right choice is unreasonable. It depends on context.

If a program is running under a supervisor, it makes sense to bail, as a new clean instance will become available.

If it was the code of the life support machine that displays data on the LEDs that I was connected to, I prefer that it solder on rather than stop functioning altogether because data for one of the digits was out of 0-9 range.

I'm sure there's been many instances where spacecraft had partially malfunctioning software that was remotely corrected because it didn't entirely give up but rather continued to accept input.

Re:1/0 = 0 by WalterBright on Saturday August 11, 2018 @04:46PM (#17741428)
Aircraft and spacecraft all have backup systems for critical software. When software self detects a bug (such as an assert failure) the offending computer is shut down and electrically isolated, and the backup is engaged.

What is absolutely NOT done is soldiering on with a computer in an unknown state.

Any software system that is life critical is either designed this way or is very badly engineered. I've written a couple articles about this:

https://www.digitalmars.com/articles/b39.html https://www.digitalmars.com/articles/b40.html

Re:1/0 = 0 by karmakaze on Monday August 13, 2018 @09:46AM (#17750000)
And what if the program is the lowest level and getting into an impossible state due to a hardware issue (bad bit). At some level a program has to soldier-on.
Re:1/0 = 0 by WalterBright on Monday August 13, 2018 @05:28PM (#17753767)
Then it sets an I/O pin that pulls the reset switch.
Re:1/0 = 0 by karmakaze on Tuesday August 14, 2018 @08:25AM (#17757227)
I cant tell ic you're being serious. The appropriateness of that would depend on whether the hardware condition was persistent in which case we have a reboot loop. It doesn't seem resonable for software meant to run in a harsh envirinment to assume prefect rumtime conditions. Do probabilitites and tradeoffs ever get considered when faced with inconsistent state?
Re:1/0 = 0 by blub on Sunday August 12, 2018 @06:54AM (#17743852)
If the program is designed to "soldier on", then why not? The question is how is the program designed to react in face of failure: should it fail safe? And is it safe to crash on any error?

My point of view is that a program should abort if it encounters an irrecoverable error, such as imminent memory corruption. However, if it's designed to fail functional, then it could continue to work, either by reinitialising itself, or by aborting the current operation. However, it must be said that it's hard to design such programs, so the option of bailing out is very attractive.

Example: an out of bounds access is a typical programmer error. This should probably result in an abort during development, so that it can be fixed. However, it is possible to not want to abort just because something is not accessible when in production. The program could instead raise an exception and clean up the call stack up to its initialisation point, then notify the surrounding system that a problem was encountered and resume service. The surrounding system could then keep track of the encountered issues and try to perform a recovery after a threshold is reached: this is how Android recovery works for e.g. crashing system services, if I recall correctly, it first cleans some application settings, then system settings, then does an OS reinstall. Now in this example the trigger condition is a crash, but it doesn't necessarily have to be like that.

Re:1/0 = 0 by boomlinde on Monday August 13, 2018 @07:09AM (#17749255)
I can't say that I'm firmly in either camp.

What seems much more important to me is that the behavior is well documented. If I am working with a system where 1 / 0 is defined to be zero, I will deal with that just as I deal with other peculiarities like 1 / 3 being 0 and 1.0 / 3.0 only approximately being a third in some systems. It's a practical concern like many others in programming.