While I submitted this, I would like to voice my opinion that I am against "var" in Java. People may ask, "Why should I have to enter in the type if the compiler can infer it for me?" My answer is twofold: 1) You or some other maintainer will need to know what that type is later when reading the code. Of course, "var" is meaningless, requiring you to dig back one or more steps to determine the actual type. 2) You don't actually need to enter in the type, any competent IDE can do it for you.
So I'm not sure what we are saving here. When has the time spent typing in code ever been a bottleneck in software development anyways?
This is my feeling from having worked extensively in Java as well as languages that support "var": C# and Swift. I feel like my productivity goes down when I have to support code that uses inferred types. There also seems to be a performance hit when compiling code with inferred typing, although that may be circumventable with better compiler tech, who knows.
* I write code a lot more fluidly with var. When I go back to writing non-var code (enforced by some departments) I find that it breaks my focus on solving the problem at hand. I end up writing my code with var and then going back and replacing my vars with the type names.
* I find code a lot easier to read. I can understand the flow of the logic easier, the variable names are enough. Unless you have the type definition memorized, just knowing the type isn't going to help you much. You're going to need an IDE either way.
* Refactoring is easier. For example, changing the return type of a function from array to list means a lot less code needs to be changed as a result if the callers were using var. The compiler will tell you if there's any instances where a caller was using an incompatible property.
* Reviewing is easier. Your change set is a lot smaller when changing a type name.
Seriously you won't miss it when it's gone. People also used to prefer Hungarian notation.
Counter-argument to changing the return type of a function with var:
You can change the return type of the function to another type that might break some assumptions later in the code.
For example, if the code assumes it has a type Foo with a length field, and you change the return type of tha function to a Bar without that length field, the compiler will complain that Bar doesn't have a length field, rather than complaining about trying to assign a Bar to a variable of type Foo.
Even worse is if the Bar type changes the symantics of that length field (perhaps going from the number of elements to the max index of elements, causing an off-by-one error), the code could break silently.
That's mostly a strawman argument, however it is worth noting.
I mentioned the first case in my post - using var is better because there’s minimal code change when refactoring and the compiler will tell you any incompatibilities to look into.
For the second case, var or not, it doesn’t matter. If you’re changing the meaning of a property it’s common sense to ‘find all refernces’ and review where that property is used.
In your example, after you manually change the return type at the caller to Bar from Foo, the usage of Foo.length and Bar.length remains and nothing will help you if they changed semantics with neither inferred nor explicit types. The access of the length field is still valid in either case.
This is an old argument. A counterargument is that if the IDE can auto-insert the type for you, it could also show you the type on demand.
But this assumes that when reading code, you're always using a tool that can show you the type. Auto-inserting is only needed when writing.
One thing to be wary of is that even when the compiler shows you the type, (in an error message, for example), if it's complex, it will be difficult to understand. If you want to write simple code, perhaps it's better to avoid or encapsulate complicated types?
Maintenance programmers already have to deal with concealed mystery types, every time you do
f(g())
it's just syntax sugar for
var temp = g()
f(temp)
Now at least programmers aren't tempted to do gratuitous function nesting rather than introducing a variable just to avoid cluttering code with an unimportant type name.
I felt the same exact way about Rust. A few years later, it's totally liberating. I do get lost from time to time, but the savings relative to spelling that stuff out is enormous. IDE support where you mouse over and it tells you what the inferred type is even better, IMO, as you get the "right" answer with full precision not the "coerced" wherein you may have erased some type info.
People said the same thing about auto in C++. Few years later and everyone I know (and codes C++) loves it and uses it all the time.
Its a great addition and like lambas will certainly improve how we write Java
Your comparison is surprisingly apt, since both languages commonly have long type names (std::vector<int>::iterator, etc.) and I often find that code that does type inference often reduces clutter because I don’t have to know the exact type that something is, but by looking at the code I generally can know what the “high level” type is.
My experience with C++ is that auto sucks when you're reading someone else's code, and while it's a useful tool it's one that should be used thoughtfully.
That seems to go against the OP's example benefits of very complex types or actually impossible types. The former will not be obvious from the assignment by nature of their complexity, no? The latter... well, being impossible without `var` implicit types is probably often also not obvious.
Unless you are writing C# in notepad, just hover the mouse over the variable and you get the type. Even when there is an ambiguity, it's really a minor inconvenience to whoever is reading the code.
> "var" is meaningless, requiring you to dig back one or more steps to determine the actual type.
> You don't actually need to enter in the type, any competent IDE can do it for you.
These two points contradict each other. Any competent IDE can visualize the inferred type even if you don't spell it, for example as a tooltip.
This is not (only) about typing, it's about the visual noise and redundancy caused by explicit types. Plus, as the article illustrates, the ability to give names to expressions that have very complex types, reducing the need for type erasure.
Yes. By not having them there makes them easier to read and understand. Seriously.
It is often the case that very simple stream operations end with ludicrously complex type signatures. By being able to var them it makes them far simpler to read.
C# developer here: my own take is that there are times when vat makes code harder to read, but your team should value identifying and avoiding that. In any event, it’s still perfectly possible to elude type information using techniques such as never declaring a variable in the first place. From this perspective it’s just one more tool in the armoury for writing readable code.
In its favour: It’s surprising how much code doesn’t really need the types written out to be readable (a discovery that will shock Python developers not at all). Furthermore, refactoring said code has less busywork in it.
As a side note: There’s one weird benefit of var not mentioned in this. Namely, you’re guaranteed there’s not going to be a cast. This is a serious problem in C++ where assigning the result of a function to a variable can have arbitrary side effects. (It’s not that bad in Java since pretty much all you can do is cast to interface.)
I'm glad that I'm not the only one in the Java community who is extremely against `var`-like constructs. Large type inference is an anti-pattern.
People usually fight this with "why would I need to type it if the compiler can figure it out!?" but those people don't understand the cardinal rule of software engineering: code is not for the compiler or the computer to understand, it is for the programmers to understand. If this wasn't the case then more people would be using APL or similarly esoteric languages.
Adding the extra effort of recursing down the rabbit hole to find the first type being used does not sound like it will make Java more friendly.
Have I saved you any extra-effort here by specifying the types? You have no idea where these types came from or how they are defined. So why is this useful? 'Go to definition' works just as well on var.
Yuck! I need to read that entire thing to have any clue as to what that produces or how I should go about using it. It is compact but it is not understandable. The "Magic" of Java is clean abstractions with no voodoo. PHP/Ruby programmers like their frameworks who hide code from the developer, which layer on complexity, and which "just work" (until they stop). In a proper Java project anyone can visually see, in any part of the program, what is attempting to be done and what is being linked up to what. A big portion of this is types.
Is all you need to read from your example. If we were to expand your ideal to a function it would look like this:
void someFunction(var customer) {
var account = customer.GetAccount();
var transactions = account.GetTransactions()
}
I can honestly tell you that it is impossible for me to know what will happen in this program unless I go through and look at every single code path that reaches this function. Once you have entire functions, or units of code, that are using `var` it becomes entirely unmanageable. Is a string getting into `someFunction`? What about a `Trader` or a `Banker` making it's way into there rather than a `Customer`? Would you know this was happening as both Trader and Banker implement .GetAccount()?
It's no surprise though that in the world of null-propagation haven that the language maintainers would decide to expand this class of problem to all object types! Why should the null primitive be the only thing that gets to ruin our day?
That code is much more scannable. The ability to scan code, to quickly glance at a method or a class and grok it in under 30 seconds is paramount. In large projects I've worked on any code that fails the 30-second test is immediately rejected at code review. Having to hover over every variable to see the types will make scanning such code in an ide a very tedious process. It will make most code review tools (that don't support such functionality) much, much less useful. I fear for the 30-second test and scanning code.
I find it much easier to scan code that uses var. Type definitions (sometimes incredibly verbose) do not help readability. And knowing the name of the type is not the same as 'knowing' the type. You still need to go to the type's definition to know what it is. Unless you have all the types in your application memorized.
This is a poor argument for var. Competent Java programmers use the extract-to-variable keybinding (Cmd-Opt-V in my environment); it even guesses the variable name correctly.
Having the type name doesn't tell you anything about the type itself. Unless you have the type definition memorized, having the type declaration there is not very useful.
If I replaced the types above with var, you would still be in the dark as to what an Account and/or Transaction is without going to the type definition.
Eh? There's a lot of information here, even without familiarity with the code (and really, most people have at least a partial understanding of the codebases they work on).
I can see that the thing returned from getAccount() is an Account object! It's not an accountId, or the name of the account, or an AccountDTO, or an Optional<Account>, or any of the dozen other things I've seen people return from methods named getAccount().
Depending on how methods are named, the implicit documentation can be very helpful:
Account from = transfer.getFrom();
I don't think that var is particularly evil, but I think it has a poor case for existence. Meh.
...tells you nothing. Is transactions iterable? Is it an enum? Is it ordered? Are duplicates allowed? Maybe you're returning a basic type? Maybe it's another pojo?
The type definition alone wouldn't tell you if it's an enum, ordered, or no duplicates allowed...
The writer would know what type they're working with through intellisense. The reader would know it's iterable based on just reading the code that follows it..
So again, what does the type info give you? Most of what you mentioned you wouldn't be able to figure out from just a type name...
'var transactions' is enough information for most people to understand it's a list of some sort. If it really matters to you then use the IDE. The full type info is just not that useful. That's why most languages are moving towards inferred typing - typescript, c#, java, c++, rust, go, scala, etc...
Why as a maintainer should var make the type harder to know? It's right there on the screen when you are initializing the variable and hopefully you're naming your variable something to make it obvious (not Hungarian notation).
If you your method is too large to fit on the screen, the method is probably too long.
But what competent IDE doesn't allow you to just hover over the variable to the know the type?
I quite like type inference in typed languages (e.g. Go, Kotlin etc), and when I read about it coming to Java I wasn't that fussed, just saw it as a nice to have.
It didn't occur to me before I read this post that the complex, chained generic types that you can sometimes get with "builder" like patterns (e.g. SQL generators) can become incredibly complex, so this would tidy that up quite nicely
Well, I don't like streams in Java, they make code much slower, it looks fancies, but in real time, streams are often abused and make code run longer in almost every case I measured (I took some methods from Github, SO and reddit comments). Equivalent for loop/for-iterator loop is much faster. Solution: you don't have to use it, you can educate others how to use it properly.
It is possible for a compiler to optimize long method chains into the equivalent imperative code. The rust compiler is an example, and in the case of the iterator trait, it can actually be optimized better than the equivalent imperative code, by removing checks when indexing into the array.
The default Java compiler doesn't do much optimizing. This is intentional, so there would be incentive to make the JVM really smart (a great decision, I think). But it's possible that the JVM doesn't optimize streams that well at this time.
Do you have any benchmarks for particular slow use-cases? I used to think the same, but in my quick benchmarks streams were as fast as handwritten code (may be a bit slower, but not much slower).
You can change concrete types of methods without refactoring variable declarations. For example ,its nice when dealing with collections without referencing them as an interface. You might think "whats so wrong with using the interface as the declared type?" In C# you can run into issues with using interfaces as things like enumerators are objects in the interface but concrete types can use optimized structs.
Can't say I'm happy about this. Type inference really doesn't belong in Java in my opinion.
Not only is it ambiguous to developers who might be maintaining the code later, I find it much worse for readability. People tend to start writing OO code like it is Javascript which is is never good.
The IDE is Java's biggest strength, and it matters.
I've worked with plenty of engineers who are absolute masters of vim and emacs; their fingers fly on the keyboard. It looks impressive but even the best of these people look like rank amateurs compared to the people who have spent equivalent time mastering IDEA or Eclipse. With a good IDE the code practically writes itself. This is a real productivity gain, and needs to be considered as part of the value proposition of the language/environment.
I think vim/emacs vs. IDE debate is similar to that playing shooters on console vs. desktop: there's no way a joystick (a control with relative position control) could be replaced with a mouse (a control with an absolute position control) with no loss of functionality.
I don't like the fact that `var` breaks class hierarchy. I can write `List l = getList()` and then variable `l` will have only methods from `List`. If I'll decide to change `getList()` return type, it'll be easier to migrate the code. With `var` variable `l` probably will have something like `ArrayList` type and I can accidentally use methods from `ArrayList`, even if I don't really need them, tying this code to concrete class.
It's obvious that everyone will use `var` everywhere, so using `var` in one place and explicit type declaration in another probably would be even worse.
The problem here is not the `var` keyword but the `getList()` method that is leaking implementation details by having a concrete class return type instead of an interface.
But it's a good style: return concrete class, so if I really need those implementation details, I can have them without casts and potential runtime errors. Return as concrete type as possible and declare variable for holding this result as abstract as possible.
If I commit to returning an ArrayList then it's not backwards compatible if I want to change to a different concrete List. My experience has been that if you return a concrete class then people will rely on those details even when they don't need to. And if you're writing a library that others depend on then you don't even know how they're using your returned value.
Of course if people are doing unsafe downcasts because they need the guarantees of a particular implementation then you should just return the ArrayList directly. And fortunately this change is backwards compatible, so there's no need to worry!
On your point about the IDE helping you, there are two use case.
object i = new object();
I agree you only type once, the IDE will suggest object after new. But in that case the type is needlessly redundant, var could be use without making the code any less readable.
object i = myfunction();
Here the object is sementically useful but given that you have to type it before you type the name of the function I don't see how the IDE can possibly help you. Not only that but unless you know the return type of that function by heart it forces you to go check it out before you even start the line. And if you are using generics (or valuetuples) that could be a long type name.
For case 2, a modern IDE like IntelliJ has completion suffixes. So you can type:
myfunction().var<TAB>
and it would expand to
object i = myfunction();
where i would be highlighted so you can immediately type the name of the variable and when you press enter the cursor is placed after the completed statement.
I agree. It seems very clear that code readability is reduced by var-like type hiding. When I'm doing code review and I see "var address = contract.getAddress();" what is the type of that variable? I have no idea.
Not sure how this helps. I pretty much knew 'getAddress' would return some sort of Address structure. It's not like knowing the type name tells you what properties are on it. So what's the point?
When did programming suddenly shift away from "the programmer should be aware of what they are doing?"
Hell, I can't count the number of times I've had to troubleshoot bad imports/classpath management brought about by some genius letting his IDE do his thinking for him.
Making it harder to program is not synonymous with making the programmer 'more aware'. I'd argue the opposite. It's much harder to figure out the source of or find all references of something without an IDE. An IDE with intellisense makes the programmer much more aware of what his/her options and navigate large code bases quickly.
String searching text files for definitions or references is just crude.
This, on the other hand, seems perfectly reasonable to me:
var c = new Customer();
Type inference works great imho to avoid specifying the object twice using a normal constructor, not a factory function, and in a codebase where actual classes and not interfaces are specified as method paramethers, though I suspect this is an antipattern ;)
Why would an address be a string? That's poor use of types. I would expect that getAddress() returns an Address. If it did return it as something non-obvious, than I would expect that to be expressed through the interface and thus the call would be getAddressAsString().
Any sufficiently sharp tool can cut deeply in the hands of the untrained. The answer isn't to dullen our blades, it's to find better apprentices and journeyman to work with.
The answer also is to have safety features. A chainsaw has to be sharp, but also has to have a chain catcher, kickback protection, etc.
I don’t think requiring programmers to always write out the type of a variable on every declaration is such a feature, but can see arguments for requiring them in some places where compiler could infer them. Types of function arguments in function declarations are an example.
I’m OK with implicit typing when the real type is “nearby”. For instance, if 2 lines away I see “Array of ObnoxiouslyLongTypeName”, I gain nothing by having to restate ObnoxiouslyLongTypeName for an iteration loop on that array. Similarly, a method or variable name may strongly hint at what it is.
Also, verbosity in programming is definitely a hindrance to maintenance. It’s generally easier to understand what’s there when you can remove some noise.
There is no appreciable performance hit for locally inferred types given that the compiler isn’t doing much. Globally inferred types can take a hit, though most of those are based on Hindley Milner which can be pretty efficient.
If the suggested `var` is anything like that of c#, there is no performance hit. When you type `Type t = some.expression()`, the compiler has to perform type inference on `some.expression()` anyway, otherwise it wouldn't be able to tell you when you've declared the type of `t` incorrectly. Indeed, when you write the wrong type there, the error message will actually tell you what type was inferred.
It reminds me of one of Bertrand Myer's many criticisms of C++ that I read in his book on Eiffel. (Almost every page of his book made fun of C++ in some way -- it was a delightful read!)
He pointed out that there was no reason for C++ to have both "." and "->", because the compiler always knew which one was required, and it only gave the programmer the opportunity to make a mistake, and a lot of extra effort changing every line of code if you change your mind about whether to use a pointer or not.
The definition of whether a member is a pointer or an inlined struct/object should only be one place in the code: in the class declaration, not scattered around every line of code in the program that uses it.
C++'s excuse was that it was trying to be compatible with C. Of course Java side-stepped the problem by not supporting embedded structs, but C# got it right.
I vaguely remember that there was some annoying guy on comp.lang.c++ years ago who relentlessly campaigned for "operator .", because he was sure that it would somehow make C++ amazingly powerful and easy to use. But many people seemed to disagree with him and had problems with it, because it never happened.
Anybody remember what the controversial benefits and problems of "operator ." were? Here's something I just found about that:
But that's coming from the same madman who wrote the proposal for "Generalized Overloading for C++2000" that actually let you overload whitespace with "operator ' '".
The big part of local type inference, which even Java couldn’t ignore, is the inference of method and function type parameter bindings for each call site. There should be some cost there, but it’s probably not much unless you are implementing something like Scala.
Not the poster, but my vote would be that it gets far more credit for readability than it deserves.
The dynamic nature helps a fair bit for some actual readability. But in large, I think it is more good marketing and a loud opinion that it is readable that makes people think it is readable.
I’m only an amateur Python programmer so grain of salt please.
Python is excellent at being intuitive and readable for the original programmer or when you’re skimming the code looking for broad logic.
Not so much for the maintainer. The programmer needs to keep a lot of state in their head to make up for the type system. If you don’t have good tests, it is even harder.
Its so funny, watching Java fans talking about silly things like `var` whilst their language gets more obsolete every day. I'm so sorry, but basic type inference won. Can you please go away so we can get a good ecosystem and a good language (with first class support VM wise) to go with it in a single package?
On a side note, its getting very tiring to rewrite the whole ecosystem every time a language is being annoying. Can the CS types please work on this real world problem a little instead of going knee deep into homotopy type theory? Please solve this somehow: allow engineers to leave a language without leaving its library ecosystem - lets make libraries super-portable, easily.
I'm not sure I understand your second paragraph, many (most?) JVM languages (Kotlin, Scala, Clojure) have Java interop, you can leave Java "the language" while keeping Java "the library ecosystem".
re: Clojure, the stack traces I think explain the issue (they're pretty terrible, you can see the Java guts).
With Scala its probably better, but still the impedance mismatch shows here and there (the Java ecosystem doesn't use case classes)
Not sure what the situation is with Kotlin, but I'm guessing its the best because its closest to Java semantically and made by JetBrains who pay extra attention to usability for developers.
I should've probably avoided the bitter sarcasm, but I've seen this `var` argument over and over for over 10 years now and I can't believe its still around, even when its very clear its bringing the language down (you can't name anonymous object types). Local type inference does not hurt readability since in > 90% of the cases the expression on the right side contains more than enough information for a human reader to infer the type too. This is often so pronounced that Java code looks downright silly: `Item item = x.getItem();`, `Array<X> xes = new Array<X>();` and so on.
Yet developers have opposed this change over and over and now that its finally getting in it feels like such a waste that it didn't happen sooner. Perhaps the ecosystem would've kept more people if it did...
Which reminded me of another waste: every time a new VM and/or language is created (like say Golang), we pour in millions of developer hours all over again re-inventing its library ecosystem. Because clearly in our new VM / language code from other ecosystems is useless junk somehow. And everyone accepts this as normal. Doesn't that ring any alarm bells in the back of our heads by this point?
I'm very disappointed that they didn't make `val` keyword. Very simple change but code becomes significantly more readable and a lot of bugs will be compile errors.
By default everything has always been mutable e.g. collections, variables. And so whilst I support val I can appreciate the difficulty in switching everyone to an immutable by default mindset. Especially given the lack of decent functional transforms e.g. map, flatMap, filter in Java.
Yes, this would be a nice addition, but I think final var might be able to stand in for it as of right now. One issue if it was adopted might be that “final” in Java doesn’t actually mean a whole lot, since there aren’t a lot of value types where mutation can be detected.
The “impossible” part of the title is an interesting one, since it mirrors how the arrival of lambdas coincided with the arrival of auto in C++ because such types are similarly impossible to represent. As I’ve mentioned in another comment in this thread, I think Java and C++ are very similar with regards for the need of type inference because they end up having a lot of the same issues.
This will be nice, but what I really want in Java is a type alias feature (what C++ calls "typedef"). This would be particularly helpful for function parameter and return types, which 'var' won't help with.
This is one of those things I really liked immediately when switching to scala. I wonder if java 10 is going to further drive adoption of scala. The difference in collections API is already argument enough to switch IMHO (hence I did)
Whereas with `var`, as you show, the compiler infers the type. Proponents of this point out exactly what you did: the type is right there, so why should the programmer have to write it twice?
My opinion is it you have the time to declare a variable, then you have the time to hit control-space to put the type. Not putting the type communicates less information to the next guy. If you "don't care" about the type, then why are you declaring a variable in the first place? Just chain the call.
I always found 'let' to be such a strange name. What on earth is appealing about it?
The usual variable declaration syntax is <type> <name>; it's not a stretch to imagine the type as 'var', a catchall type. But 'let'? Let is a verb; it should be in a place where functions, not types, go. It makes sense in "let x in {}" type expressions, and it kinda makes sense in Lisp, but I don't see any argument for it in a C-like language.
So I'm not sure what we are saving here. When has the time spent typing in code ever been a bottleneck in software development anyways?
This is my feeling from having worked extensively in Java as well as languages that support "var": C# and Swift. I feel like my productivity goes down when I have to support code that uses inferred types. There also seems to be a performance hit when compiling code with inferred typing, although that may be circumventable with better compiler tech, who knows.