Crashing would have been preferable.
1/0 = 0 is unsuitable and dangerous for anyone doing anything in the real world.
Crashing would have been preferable.
1/0 = 0 is unsuitable and dangerous for anyone doing anything in the real world.
1. Might crash.
2. Result may not be what you’d expect from conventional math.
3. Inputs and outputs are different types.
4. Nonlinear control flow i.e. exceptions.
Division isn’t even particularly special here. If you have fixed-width integer types (as most languages seem to) then this is a problem for all the basic operators.
3 and 4 are attractive solutions but can get annoying or cause more bugs. (How many catch blocks out there have zero test coverage?) Between 1 and 2, 1 is usually much better.
For cases where the programmer wants 2, you can provide alternate operators. For example, Swift crashes on overflow or with the standard operators, but has variants like &+ for modular arithmetic.
But one time an exception came at just the right time to cause the internal state and database state to be out of sync. That caused data updates in the service from that point on to start saving bad data into the database. It took a few hours to notice the issue and by that point a lot of the persisted data was trashed. We had to take down the service, restore the database from a backup, and reconstruct the correct data for the entire day.
Fortunately the data issues here were low impact, but it could just as easily have been critical data that was bad. And having a business operate on incorrect data like that could cause far bigger issues than a bit of downtime while the service restarts.
Gleam offers division functions that return an error type, and you can use those if you need that check.
They fit a list-length use case well as they work better with a piping syntax which is popular in Gleam.
[1] https://blog.nestful.app/p/why-i-rewrote-nestful-in-gleam
That, however, is still not enough to alleviate OP's concerns, which is why I've explained how the `1/0=0` problem can be entirely avoided.
I expect entirely avoiding the problem OP mentioned is enough to alleviate the concerns it raises.
Nobody is questioning your intentions. People writing apps in memory-unsafe languages don’t give fewer shits. They’re just more prone to certain classes of errors.
> how the `1/0=0` problem can be entirely avoided
1/0 problems are generally expected to be entirely avoided. This is about where the system behaves unexpectedly, whether due to human error or the computer being weird.
All I was doing was clarifying the impression OP gave.
Now that we all know the details we can make whatever tradeoff we prefer.
Its the year of the Lord 2024, why is a new language putting in such a huge footgun out of the box in its stdlib.
Yes, but is that what any given developer will reach for first? Especially considering that an error-returning division is not composable?
The language puts people into a place where the instinctive design can cause very dangerous outcome, hard to see in a code review, unless someone on the team is a language lawyer. You probably don't want one of those on your team.
I think there's a reasonable argument for gleam to have an operator that does division resulting in zero but at the very least that should NOT be "/"
- a sum type (or some wrapper type) `number | DIVISION_BY_ZERO` forces you to explicitly handle the case of having divided by zero
- alternatively, if the division operator only accepted the set `number - 0` as type for the denominator you'd have to explicitly handle it ahead of the division. Probably better as you don't even try to divide by zero, but not sure how many languages can represent `number - 0` as a type. Positive = One | Succ Positive
Nat = Zero | Positive
NonZeroInt = Positive | Neg Positive
Int = Zero | NonZeroInt
Rational = Ratio Int Positive
etc.Depending on the language, these could be implemented with little or no runtime overhead.
I know that nested radicals can't always be un-nested, so I don't think larger sets (like the Algebraic numbers) can be reduced to a unique normal form. That makes comparing them for equality harder, since we can't just compare them syntactically. For large sets like the Computable numbers, many of their operations become undecidable. For example, say we represent Computable numbers as functions from N -> Q, where calling such a function with argument x will return a rational approximation with error smaller than 1/x. We can write an addition function for these numbers (which, given some precision argument, calls the two summand functions with ever-smaller arguments until they're within the requested bound), but we can't write an equality function or even a comparison function, since we don't know when to "give up" comparing numbers like 0.000... == 0.000....
FYI I'm currently playing around with numerical representations at http://www.chriswarbo.net/blog/2024-11-03-rationalising_deno...
I've learned my lesson since, but still.
> Everything would have been just fine if dividing by zero yielded zero
perhaps you weren't making business decisions based on the reported average, just logging it for metrics or something, in which case I can see how a crash/restart would be annoying.
I imagine the problem was that it crashed the whole process, and so the processing of other, completely fine data that was happening in parallel, was aborted as well. Did that lead to that data being dropped on the floor? Who knows — but probably yes.
And process restarts are not instantaneous, just so you know, and that's even without talking about bringing the application into the "stable stream processing" state, which includes establishing streaming connections with other up- and downstream services.
I guess that's slightly more of a warning than giving 0.
Intel's 80186 produced a result like that in one special case, because of a missing check in the microcode. This could be called a bug or an optimization: the "AAM" instruction was only documented as dividing by 10, but in fact takes a divisor as part of its opcode (D4 0A = divide by 10, as listed in the documentation; D4 00 = divide by zero). The normal divide instruction - as well as AAM on all other x86 processors - check for zero and throw an exception.
RISC-V just doesn't bother doing that.
Or rather how division could be implemented. Risc-V is an abstract instruction set architecture not born from a concrete chip, like x86 was; but they are trying to make things easy on the hardware.