News for hackers. Stuff that matters.
Re:Never patterns, exhaustive matching, and uninhabited type... by vultour on Saturday August 18, 2018 @08:19AM (#17788137)
Can someone explain why you'd want uninhabited types?

> if you have to define an error type for some computation, but this particular computation can never fail, you might use an uninhabited type.

I don't see the reason behind returning a Result<String, Err> if there is no error to return. Why not just return a String?

Re:Never patterns, exhaustive matching, and uninhabited type... by the8472 on Saturday August 18, 2018 @08:27AM (#17788161)
When you implement a generic trait that is designed to handle errors but your specific case is infallible then it is helpful to indicate that the error case can never occur. Marking that as uninhabited makes it easier for downstream consumers to just eliminate all code related to error handling for that concrete type.
Re:Never patterns, exhaustive matching, and uninhabited type... by phyzome on Saturday August 18, 2018 @08:20PM (#17791697)
An example I can think of is Java's StringReader class. It implements the Reader interface, so it has to bear the "throws IOException" signature... but it's impossible for it to produce an I/O error, since it's not actually doing any I/O, just reading from a String.

That's fine if you're using it via the Reader interface, but if you're working with StringReader directly it's a pain in the butt to deal with the non-existent exceptions. It sounds like never-patterns would help with that.

Re:Never patterns, exhaustive matching, and uninhabited type... by zebraflask on Saturday August 18, 2018 @07:07PM (#17791353)
Please don't flame me if this is an ignorant question, but how is uninhabited different from Javascript's undefined? That's what I would use undefined for in JS (very loosely speaking).
Re:Never patterns, exhaustive matching, and uninhabited type... by Veedrac on Saturday August 18, 2018 @07:39PM (#17791533)
You can actually set a variable to `undefined` in Javascript. You can't set a value to an uninhabited type─there is no value that represents the type.
Re:Never patterns, exhaustive matching, and uninhabited type... by zebraflask on Saturday August 18, 2018 @07:41PM (#17791541)
I get it, so the concept is closer to declaring a variable and that's it, nothing further happens.

Thanks.

Re:Never patterns, exhaustive matching, and uninhabited type... by sacado2 on Sunday August 19, 2018 @12:42PM (#17794984)
"undefined" is a type that has exactly one possible value. An uninhabited type has no possible value.
Re:Never patterns, exhaustive matching, and uninhabited type... by runeks on Saturday August 18, 2018 @03:06PM (#17790047)
> Can someone explain why you'd want uninhabited types?

Uninhabited types are useful for expressing units.

I'm writing an app that makes heavy use of a Haskell library[1] that implements currency units and exchange rates using uninhabited types (type-level strings).

So, one dollar will have the type `Amount "USD"`, whereas one Euro will have the type `Amount "EUR"` (both will have a value of `1`).

An exchange rate from Euros to dollars will have the type `ExchangeRate "EUR" "USD"` (the first type parameter being 'source' and the second 'destination'), and its value will be `1.14` (the current exchange rate from EUR to USD).

A function for converting an amount in one currency into another currency unit will take two arguments: `Amount src` and `ExchangeRate src dst` and return an `Amount dst`.

Also, in general, it can be useful to:

1. Remove an argument from a function

2. Create an uninhabited type that, at the type level, represents the values of this argument

3. Tag the function's return type with this uninhabited "phantom" type

because now you no longer have to remember from which argument some return value was calculated/derived: it's right there in the value's type.

[1] https://www.stackage.org/haddock/lts-12.6/safe-money-0.6/Mon...

Re:Never patterns, exhaustive matching, and uninhabited type... by sasmithjr on Saturday August 18, 2018 @12:40PM (#17789267)
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/

Re:Never patterns, exhaustive matching, and uninhabited type... by zlynx on Saturday August 18, 2018 @01:11PM (#17789430)
This 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.

Re:Never patterns, exhaustive matching, and uninhabited type... by kibwen on Saturday August 18, 2018 @02:43PM (#17789900)
> 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).

Re:Never patterns, exhaustive matching, and uninhabited type... by Rusky on Saturday August 18, 2018 @01:19PM (#17789471)
I'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.
Re:Never patterns, exhaustive matching, and uninhabited type... by saagarjha on Saturday August 18, 2018 @10:50PM (#17792280)
This 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.
Re:Never patterns, exhaustive matching, and uninhabited type... by Rusky on Sunday August 19, 2018 @02:45PM (#17795601)
Yes, I know- that was my point. The comment I replied to was talking about CPU/RAM errors somehow bypassing that proof.
Re:Never patterns, exhaustive matching, and uninhabited type... by hinkley on Saturday August 18, 2018 @02:06PM (#17789699)
I 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.

Re:Never patterns, exhaustive matching, and uninhabited type... by kibwen on Saturday August 18, 2018 @02:57PM (#17789991)
I 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.

Re:Never patterns, exhaustive matching, and uninhabited type... by derefr on Sunday August 19, 2018 @05:28PM (#17796281)
Static 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.

Re:Never patterns, exhaustive matching, and uninhabited type... by reikonomusha on Saturday August 18, 2018 @08:23AM (#17788152)
Perhaps you’re making use of a parametric type provided by a library, and you don’t have control over the structure except by filling in type parameters.
Re:Never patterns, exhaustive matching, and uninhabited type... by a1369209993 on Saturday August 18, 2018 @02:03PM (#17789682)
I mostly use them as return types for functions that are guaranteed to either loop forever or kill the process:

  noreturn mainloop(State* s)
    {
    for(; s->valid ;s->itick++) do_step(s);
    die(70,"invalid internal state");
    }
Re:Never patterns, exhaustive matching, and uninhabited type... by danidiaz on Saturday August 18, 2018 @10:33AM (#17788620)
Types that represent producers of data often return a special result value at the end, of a type different from the values that are yielded. Say, a stream of integers that return a Boolean when it's exhausted.

By choosing an unhabited return type, you can express that the stream never stops producing values by itself.

Re:Never patterns, exhaustive matching, and uninhabited type... by the_mitsuhiko on Saturday August 18, 2018 @09:33AM (#17788371)
A string is the worst thing to return because it’s huge. Most people return () instead which is zero sized.

However when returning ! the compiler can optimize the entire error handling away.

Re:Never patterns, exhaustive matching, and uninhabited type... by masklinn on Saturday August 18, 2018 @12:13PM (#17789143)
> A string is the worst thing to return because it’s huge. Most people return () instead which is zero sized.

I think what they're asking is "if you're returning a Result<String, Err>" but you're making the Err an unhinabitated type (so can't ever have an error) why not just return a String in the first place?

Re:Never patterns, exhaustive matching, and uninhabited type... by the_mitsuhiko on Saturday August 18, 2018 @02:14PM (#17789747)
Because there are generic APIs that return results as part of the contract. A good example is FromStr.
Re:Never patterns, exhaustive matching, and uninhabited type... by fnord123 on Sunday August 19, 2018 @04:45AM (#17793318)
A string is only like 16bytes (length + pointer to memory in heap) or more if it's a fat pointer.
Re:Never patterns, exhaustive matching, and uninhabited type... by ChrisSD on Saturday August 18, 2018 @08:48AM (#17788219)
I've used `enum Void { }` as a placeholder for opaque FFI managed types that I pass between APIs but never inspect myself. Like Windows handles (`*const Void`) or data structures that are queried using an API rather than directly.
Re:Never patterns, exhaustive matching, and uninhabited type... by nalllar on Saturday August 18, 2018 @09:51AM (#17788448)
Re:Never patterns, exhaustive matching, and uninhabited type... by ChrisSD on Saturday August 18, 2018 @09:54AM (#17788464)
Oh wow, I didn't know this. At one time at least it was recommended, which was why I did it.
Re:Never patterns, exhaustive matching, and uninhabited type... by bjz_ on Saturday August 18, 2018 @10:31AM (#17788610)
Yeah, IIRC many years ago I added it in a document as a pattern for C APIs (I can't remember where though). Either other people were doing the same thing, or were influenced by that. Anyway, you should use `*const libc::void` these days! Sorry for the confusion.