3 ms·
Having the option to use type inference in this specific use case is nice. But I'm glad it's just for initializers. After playing with Scala, I've found out I
by zZorgz 11y ago
Having the option to use type inference in this specific use case is nice.
But I'm glad it's just for initializers.
After playing with Scala, I've found out I don't love type inference as much as I thought I did. In Scala, the compiler can infer return types of named procedures for example, but I've always found omitting these makes my code harder to read, having to read the body (an implementation detail) to see what it may be returning. It would be even worse if types were optional for parameters (ala Haskell). Other modern languages (ala Rust) have come to realization that this is a bad idea.
The same even applies to having the compiler infer types from local variables to a lesser extent. A lot of my code goes through transformations and chains, and sometimes it's not obvious what the resulting type is (and what the code is doing) if the type is not specified.
Even looking at the simple example mentioned from the article in isolation:
var stream = list.stream();
I don't know what type stream is. Sure, I can make an educated guess that it's some kind of Stream, but then knowing it's a parameterized, and then knowing what kind of type it takes is even more challenging. I would have to look at the right hand side (again, an implementation detail), or may have to traverse up even further (eg: looking up how `list` is derived in this case) to figure out for sure. Or worse yet, look up documentation. Imagine if the right hand side was composed by calling a series of procedures, then you have to traverse all the way to the right to validate the type. In the end, you have a mental burden.
The same issue applies with calling functions and assiging their result to a local variable you're initializing.
I'm happy to see type inference is just added for initializers. Whilst I haven't found writing 'Foo foo = new Foo()' that common in functional code, I see that being prevalent in Java at least. On the flip side though, if your language or use case (C++?) makes code harder to understand rather than easier by specifying types, then you may be wishing you had type inference :)
- dllthomas 11y ago> It would be even worse if types were optional for parameters (ala Haskell). The Haskell community seems pretty well agreed that top level declarations should have type annotations (arguments and return values) in code that's going to be maintained. With -Wall, GHC will even give you warnings about top level declarations without types. No reason to require it for quick (~5 min) experimental hackary or one-off scripting - in that case, add it if it helps and omit it if it doesn't, and the ability to ask what the compiler thinks the type is can come in handy as well.
- zZorgz 11y agoSeems reasonable. But now you risk the chance of reading code written by those who don't specify types, don't know better, or don't use -Wall. I think deciding what to allow and prohibit by default is an important design decision.
- dllthomas 11y ago> I think deciding what to allow and prohibit by default is an important design decision. I don't think I disagree with that, but I don't think it was made wrong here. It's true that you "risk the chance" of reading unannotated code, but I find there are times when the code I need to write is clearer to me than the type it'll have, and being able to defer annotation past compilation is occasionally a big win and often a small one. And in that worst case scenario of having to look at unannotated code, you can fall back to asking your tooling.
- zZorgz 11y agoMy own experience is that I've been running into less scenarios where I think omitting the type makes code more readable (coming from Scala). It doesn't help myself and my coworkers that it's easy (or 'lazy') to omit annotations. I often wish I was using a text editor or IDE that understood my code and could insert type annotations for me, maybe automatically, and maybe not choose to do so if the line of code would "look" more redundant (i.e: parsing variable name or constructor/initializer). Rust is one language that, while has the capability of doing full type inference, deliberately chose not to (https://doc.rust-lang.org/book/functions.html https://doc.rust-lang.org/book/functions.html): > This is a deliberate design decision. While full-program inference is possible, languages which have it, like Haskell, often suggest that documenting your types explicitly is a best-practice. We agree that forcing functions to declare types while allowing for inference inside of function bodies is a wonderful sweet spot between full inference and no inference
- dllthomas 11y agoI agree that omitting the type rarely, if ever, makes the code more readable. But readability can easily matter less than other things over the course of one compile. This is why I think the best choice is demanding top level annotations for reasonably complete code (say, every commit) and not doing so before I can ask for the shape of something, or see that it outputs what I thought, or run my tests, or see what type errors I get elsewhere. It might be marginally better to demand it by default and have a flag to disable the check. Demanding it always is more than marginally worse (which is not to say it's the end of the world - it's totally not).