Good luck. Give the avionics guys a call if you solve this at the language level.
Special values like NaN are half-assed sum types. The latter give you compiler guarantees.
From memory, I have heard "infecting all downstream" as both "a feature" and "a problem". Experience with numpy programs did lead to sentinels in the https://github.com/c-blake/nio Nim package, though.
Another way to try to investigate popularity here is to see how much code uses signaling NaN vs. quiet NaN and/or arguments pro/con those things / floating point exceptions in general.
I imagine all of it comes down to questions of how locally can/should code be forced to confront problems, much like arguments about try/except/catch kinds of exception handling systems vs. other alternatives. In the age of SIMD there can be performance angles to these questions and essentially "batching factors" for error handling that relate to all the other batching factors going on.
Today's version of this wiki page also includes a discussion of Integer Nan: https://en.wikipedia.org/wiki/NaN . It notes that the R language uses the minimal signed value (i.e. 0x80000000) of integers for NA.
There is also the whole database NULL question: https://en.wikipedia.org/wiki/Null_(SQL)
To be clear, I am not taking some specific position, but I think all these topics inform answers to your question. I think it's something with trade-offs that people have a tendency to over-simplify based on a limited view.
In my opinion it’s overall cleaner if the compiler handles enforcing it when it can. Something like “ensure variable is initialized” can just be another compiler check.
Combined with an effects system that lets you control which errors to enforce checking on or not. Nim has a nice `forbids: IOException` that lets users do that.
Only sometimes, when the compiler happens to be able to understand the code fully enough. With sum types it can be enforced all the time, and bypassed when the programmer explicitly wants it to be.
tbh this system (assuming it works that way) would be more strict at compile-time than the vast majority of languages.
import std/errorcodes
proc p(x: int) {.raises.} =
if x < 0:
raise ErrorCode.RangeError
use x
I can’t stand that there’s no direct connection between the thing you import and the names that wind up in your namespace.Oh, and as someone else pointed out you can also just `from std/errorcodes import nil` and then you _have_ to specify where things come from.
What is preventing this import std/errorcodes
from allowing me to use: raise errorcodes.RangeError instead of what Nim has?
or even why not even "import std/ErrorCodes" and having the plural in ErrorCodes.RangeError I wouldn't mind
import math
echo fcNormal
echo FloatClass.fcNormal
echo math.fcNormal
echo math.FloatClass.fcNormal
All of these ways of identifying the `fcNormal` enum value works, with varying levels of specificity.If instead you do `from math import nil` only the latter two work.
I personally prefer to make the error state part of the objects: Streams can be in an error state, floats can be NaN and integers should be low(int) if they are invalid (low(int) is a pointless value anyway as it has no positive equivalent).
It's fine to pick sentinel values for errors in context, but describing 0x80000000 as "pointless" in general with such a weak justification doesn't inspire confidence.I would agree, whether error values are in or out of band is pretty context dependent such as whether you answered a homework question wrong, or your dog ate it. One is not a condition that can be graded.
Edit: I guess they could get rid of a few numbers... Anyhow it isn't a philosophy that is going to get me to consider nimony for anything.
Not if you compile with optimizations on. This C code:
int wrapping_add_ints(int x, int y) {
return (int)((unsigned)x + (unsigned)y);
}
Compiles to this x86-64 assembly (with clang -O2): wrapping_add_ints:
lea eax, [rdi + rsi]
ret
Which, for those who aren't familiar with x86 assembly, is just the normal instruction for adding two numbers with wrapping semantics. proc fib[T: Fibable](a: T): T =
if a <= 2:
result = 1
else:
result = fib(a-1) + fib(a-2)
Integer is the only possible type for T in this implementation, so what was the point of defining Fibable?I think the fib example is actually cool though. Integers are not the only possible domain. Everything that supports <=, +, and - is. Could be int, float, a vector/matrix, or even some weird custom type (providing that Nim has operator overloading, which it seems to).
May not make much sense to use anything other than int in this case, but it is just a toy example. I like the idea in general.
Nim is Choice in many dimensions that other PLang's are insistently monosyllabic/stylistic about - gc or not or what kind, many kinds of spelling, new operator vs. overloaded old one, etc., etc., etc. Some people actually dislike choice because it allows others to choose differently and the ensuing entropy creates cognitive dissonance. Code formatters are maybe a good example of this? They may not phrase opposition as being "against choice" as explicitly as I am framing it, but I think the "My choices only, please!" sentiment is in there if they are self-aware.
However, the base case being 1 does not preclude other types than integers, as cb321 pointed out.
I have long thought that we need a NaI (not an integer) value for our signed ints. Ideally, the CPU would have overflow-aware instructions similar to floats that return this value on overflow and cost the same as wrapping addition/multiplication/etc.