Hacker News new | ask | show | jobs
by steveklabnik 2440 days ago
The lack of top level inference is a key part in getting great compiler errors: top level inference can be convenient, but it also leads to spooky action at a distance errors.
1 comments

Interesting. I'm sure you know more about it than I do, but in Swift for example, it seems like this mostly results in an error like "unable to infer the type of X in the current context" which is maybe not the simplest error in the world to figure out, but it's really not too difficult to unwind as compared to say, cryptic C++ template errors.

I suppose you can't have everything, but in my experience top level inference makes code a lot easier to read because it removes a lot of noise and redundant information while largely leaving the "intent" of the code in place.

Yeah I mean, it really depends on the exact type system features and implementation, but that sounds like a different error than the kind I'm talking about. For example, consider this code, imagining Rust had type signature inference:

    fn foo() {
        String::new()
    }
    
    fn bar() {
        foo()
    }
    
    fn main() {
        let s: String = bar();
    }
Imagine you modify foo, and now you're accidentally returning a Vec, not a String:

    fn foo() {
        Vec::new()
    }
    
    fn bar() {
        foo()
    }
    
    fn main() {
        let s: String = bar();
    }
You'd get an error like this:

    error[E0308]: mismatched types --> src/main.rs:10:21
       |
    10 |     let s: String = bar();
       |                     ^^^^^ expected struct `std::string::String`, found struct `std::vec::Vec`
       |
       = note: expected type `std::string::String`
                  found type `std::vec::Vec<i32>`
You changed foo, but the error points to bar, in main. None of this is related to the code you changed. But in Rust today, you'd get

    error[E0308]: mismatched types
     --> src/main.rs:2:9
      |
    1 |     fn foo() -> String {
      |                 ------ expected `std::string::String` because of return type
    2 |         Vec::new()
      |         ^^^^^^^^^^ expected struct `std::string::String`, found struct `std::vec::Vec`
      |
      = note: expected type `std::string::String`
                 found type `std::vec::Vec<_>`
This points right to the issue: you're returning a vec, and not a string, in foo.

There are other issues too; global inference and subtyping have problems. While Rust doesn't have subtyping generally, we do in lifetimes...

Beyond all of that though:

> it removes a lot of noise and redundant information while largely leaving the "intent" of the code in place.

Rust's perspective on this is that the type signature is what communicates your intent. It's like a unit test. You write down what you expect, and then the compiler's job is to check that your code does what you said you were going to do. This seems like philosophically at odds with what you expect, which is fine of course, but is probably where a lot of the divergence comes from.

Interesting. Thank you for taking the time to elucidate the issue so clearly.
Any time!