5 ms·
Consider the Ruby Gem, Fog[0]. It is designed to abstract different services away so that developers can just program against Fog and pick/choose different serv
by sasmithjr 8y ago
Consider the Ruby Gem, Fog[0]. It is designed to abstract different services away so that developers can just program against Fog and pick/choose different service providers as necessary. It's possible that for specific implementations of the fog-service boundary, an error cannot occur that definitely can occur in other implementations.
Uninhabitable types allow the implementer to tell the compiler "I cannot provide an error here", giving the compiler more information to work with when optimizing. For example, the compiler now knowing that the function call cannot return an error may make it so that the function does only return the string after optimization, and the compiler can also drop the error checking code in the caller because it's unreachable.
[0]: http://fog.io/ http://fog.io/
- zlynx 8y agoThis kind of thing has been plenty annoying in C++ code. Things like switch, default, abort that used to catch RAM or CPU errors and provide useful information now get optimized away and execution continues in the face of impossible values. Removing checks for "cannot possibly happen" cases isn't usually worth the tiny time savings.
- Rusky 8y agoI'm all for leaving asserts in release builds, but leaving in dead code based on uninhabited types isn't gonna help you catch RAM or CPU errors. You'll just continue into the error branch with an impossible value instead. It's the wrong layer for that.
- saagarjha 8y agoThis isn't an assert, it's not a runtime check; it's done at compile time. You need to prove to the compiler that your code can never reach this point; common ways of doing so are by calling exit or inserting an unconditional infinite loop. If your underlying computational platform is broken, this isn't going to save you, but I don't think there's much you can do at that point.
- Rusky 8y agoYes, I know- that was my point. The comment I replied to was talking about CPU/RAM errors somehow bypassing that proof.
- hinkley 8y agoI think ones of the design books I read years ago talked about a piece of building infrastructure that “could not fail” so they incorporated it directly into the concrete pour. Of course it failed, and it was a royal pain to get to the bits that failed because they were embedded in concrete that was probably holding the building up. You build for robustness because you might be wrong, the new guy might not understand, or a silicon isotope might decay at just the wrong time and flip a bit. Circuits are analog. They just pretend very very hard to be binary. Weird shit can happen that shouldn’t be possible.
- kibwen 8y agoI think this is conflating two different concepts, only one of which concerns uninhabited types. If the programmer "knows" that a condition is impossible, but can't prove it to the Rust compiler, then they can the `unreachable!` macro to signal their intent to the compiler; in this case the compiler knows that it cannot trust the programmer (because it has no proof) and so generates code anyway that will handle the impossible condition (in this case, by panicking, which is the typical Rust reaction to a signal that the programmer's assumed invariants have been violated). On the other hand, the use of an uninhabited type means that the programmer has to prove to the compiler that the type is uninhabited, in the same way that declaring a function as returning a String requires the programmer to prove that the function actually does return a String. If we have a function that returns a String, the compiler does not generate any code to handle the case where that function returns an integer instead; that just doesn't make sense, we've used the type system to prove that can't happen. Likewise, it wouldn't make any sense for the compiler to generate code to handle the case where a function returning an uninhabited type returns anything at all.
- derefr 8y agoStatic languages in general aren't good at catching impossible conditions. If anything, you probably want something more like Erlang's supervision hierarchy where pattern-match failures (due to a cosmic ray, or anything else) kill the actor-process that was running, and then the actor's parent reinstanciates it and it tries again. Then you just need to draw fine-grained failure boundaries (i.e. what things end up in new actor-processes) to ensure that crashing out a process doesn't waste too much work unnecessarily. Or the Mars-rover "six CPUs on separate NUMA nodes are each running a copy of the program, and quorum-consensus on the result of each function-call" strategy, but that's a bit expensive.
- kibwen 8y ago> execution continues in the face of impossible values I don't know about C++, but it may be doing something different from Rust here. In Rust, the ! type (which I pronounce "Never"), isn't just a case of "I, the programmer, don't think any value will exist here"; the compiler forces you to prove (just like every other type is a proof) that a value can't exist. The Never type can't be instantiated (it has no constructor), and functions that "return" Never must prove that they, er, never return; imagine a function whose body is just `loop {}` (or the stdlib function process::exit, which cannot return by dint of killing the callstack when the process dies).