Well on the other end of the extreme we had Java, which had an awful lot of this: HashMap<String, List<int>> hashmap = new HashMap<String, List<int>>();
Latest Java lets you omit the left type annotation (I hear, haven't tried it): var hashmap = new HashMap<String, List<int>>();
And in many languages the type parameters on the right can also be inferred --- so long as later code determines them --- so you get something like: var hashmap = new HashMap();
Hopefully we can all agree that the first option is needlessly verbose. There's more contention between the last two, I think.My preference would be to do some type inference, but maintain the property that you can tell the type of every expression without looking outside of it (except perhaps for an immediate enclosing function call). This requires, for example: - The third option isn't allowed, you need to write the second option instead. - You must annotate function argument types. - In Rust, you couldn't write `.collect()`, you'd instead write `.collect::<Vec<_>>()`. - The `x := 10` example is actually somewhat ambiguous. If the language fixes the type of `10` then `x := 10` is legal. If it's an unspecified type (as is typically the case), you'd have to write the type down. |
For that case I like a type signifier as part of the number literal expression, like this: `x := 10f32` or `x := 10i32`.