3 ms·
Let's avoid using the term "subtyping", which as you say is irrelevant here. The reason you need diverging functions to satisfy arbitrary type obligations (i.e.
by kibwen 21d ago
Let's avoid using the term "subtyping", which as you say is irrelevant here. The reason you need diverging functions to satisfy arbitrary type obligations (i.e. to coerce to any other type) is because otherwise anything as simple as `let x = Some(42); x.unwrap();` just completely fails to compile, because `unwrap` is internally just:
fn unwrap<T>(t: Option<T>) -> T {
match t {
Some(foo) => foo,
None => panic!()
}
}
...and this function couldn't otherwise typecheck because it doesn't return a `T` in the `None` branch. You need coercion here.
- kccqzy 21d agoNo you don’t need coercion. You only need polymorphism. The type of `panic!()` could be an arbitrary U, which unifies just fine with the type T here. Generally languages with such polymorphism have a never type only because they don’t also support impredicative polymorphism.
- kibwen 21d agoAnd then once you have `fn foo<T>() -> T`, what do you write in the body that allows it to typecheck?
- kccqzy 21d agoYou still write `panic!()`. It type checks using polymorphism only, without any coercions.
- kibwen 21d agoBut `panic!()` is just a macro invocation that needs to expand to something, and the question here is what that something ought to be in order to produce a valid program. Currently it expands to this: https://github.com/rust-lang/rust/blob/98fd715edd3a0a5aa8f2041d7e20a3143b0fc0d3/library/core/src/panicking.rs#L138 https://github.com/rust-lang/rust/blob/98fd715edd3a0a5aa8f20... , which is a function with a return type of `!`, which has indicated a diverging function since long before Rust even considered having a first-class never type.
- kccqzy 21d agoRight. And the whole discussion boils down to, why can’t this function return type be polymorphic in the first place? This avoids the never type, the coercions from the never type, and all the issues caused by that described in the article.
- jadenPete 19d agoHere’s another example: fn f<T>() -> T { loop { println!(“Looping”); } } This won’t compile. How do you express to the compiler that this is correct (because the function never completes) without a never type? Rust also doesn’t support generic function types, so a callback function returning `!` is possible, whereas one accepting a type argument and returning that type is not. You could do something clever with traits, but that requires extra work. A lot of these things are achievable without `!`, but the compiler still needs that type internally and it pieces so nicely into the type system that I don’t see the point of bending over backwards to avoid it.