I hope this gets added to the language, we use Lombok heavily where I work and find it pretty great with it's val class that allows type inference, although it requires an explicit import and can't be used within Lambdas.
The website isn't the prettiest but Lombok is a fantastic addition to a Java codebase and massively reduces the amount of boiler plate often associated with Java. A list of Lombok features https://projectlombok.org/features/index.html
Java 8 + Lombok + Guava == happy developers (well at least in my case :) ).
Lombok has always been a hacky set of language extentions. It says a lot about the need for more language features in Java that developers have been willing to resort to using it. Adding val and var to Java will be a big step forward.
Sure, no language is perfect but I've never ran into problems using Lombok in production. I'd gladly welcome the majority of its features being part of the core language though! :)
Apache Groovy used to have the === operator in its earliest versions, but dropped it in favor of using a custom .is() method. The meaning of == is different to its use in Java, catching out many -- "happy" is not the word I'd use.
Apache Groovy has some long-standing governance issues, e.g. the groovy-lang.org DNS is owned by a single individual. Other languages such as Kotlin and Scala have more, er, responsible governance.
A while back, I was trying to explain in what ways Scala is more typesafe than Java for a friend (which was one argument to why I prefer Scala so much), and ended up realizing how important type inference is in this regard.
The reason was this:
quite often type signatures correctly modelling a domain ends up being pretty hard to read. In a good and proper API, this happens in particular whenever something is composable and is, in fact, composed (if one can say that). I suppose one can say that, for Scala, it took a bit of time before people figured out how to do this properly, rather than just hacking the type system, but I feel that the ecosystem is more or less there now... YMMV though...
As an example: in something like Slick (a functional-relational mapper) in Scala you end up with monads to model your SQL query. One advantage this has is compile errors whenever you're doing something wrong (i.e. more typesafe), but the types that a SQL query representation ends up with will necessarily be pretty hard to read and practically impossible to write (you could have a Query of a long chain of other types for example if you're querying multiple columns).
In this example, somebody writing the query might not care about the type of the query. They will care about what the query will end up returning when executed though. I suppose it will lead to trouble when you can't read the type signatures well enough and try to figure out the compiler error, but I reckon it's better to have an error, than to see it fail runtime (at least in my world :). It's easier and safer to do maintenance too, since the compiler and IDEs can know how things should work.
In any case, you could conclude that, in a statically compiled language where you do not have type inference, you'll end up trading in preciseness in the model of the domain, in order to have readable code from the callsite. In turn, this leads to less typesafe code.
One challenge that Java will have even when implementing this though, is how to move the entire ecosystem to something that is indeed more typesafe. This only works if the APIs are doing this right.
> A while back, I was trying to explain in what ways Scala is more typesafe than Java for a friend (which was one argument to why I prefer Scala so much), and ended up realizing how important type inference is in this regard.
Type inference has absolutely nothing to do with type safety. Programming languages don't become more or less statically type safe depending on whether they support type inference.
Type inference only enables to omit type annotations in certain parts of your code. Your values are just as typed as if you had used that type annotation.
As for the original point you were trying to make, I would say Scala is no more type safe than Java. They are about on par in how they allow unsafe expressions to type check. Scala's type system is certainly richer than Java's, but that's a separate point (and maybe the one you were trying to make).
> In any case, you could conclude that, in a statically compiled language where you do not have type inference, you'll end up trading in preciseness in the model of the domain, in order to have readable code from the callsite. In turn, this leads to less typesafe code.
Absolutely not.
Omitting a type in your source doesn't remove the type from that variable. The code is exactly as typed with that type annotation as it is without.
I think his argument is that without type inference, programmers will avoid more complex types, and therefor model their domain with less precise types.
The curious case of the broken inverse: you won't get more type safety by having type inference, but you are likely to end up with less type safety when you are not having it ;)
I have seen no evidence of that. With that logic, Java programmers would use "Object" instead of "List<String>" because they have fewer characters to type?
It makes more sense after you have deliberately weakened the typing of an API because you felt that you were overstretching your co-developers (and/or your own) patience for nested angle brackets.
The basic idea is that type inference would shift the sweet spot for the right amount of typing upwards.
Hmmm. Yes, you're right. Typesafe is probably the wrong word to describe this according to you and wikipedia :)
The problem I tried to describe though is how feasible/typical/normal? it is to catch more or less (type) errors on compile time...
Like here, where I basically my point is that it is too hard to use Java's type system to it's full extent because it becomes too cumbersome to actually use it on the callsite.
Puh! Wikipedia also states:
'Type safety is sometimes alternatively considered to be a property of a computer program rather than the language in which that program is written; that is, some languages have type-safe facilities that can be circumvented by programmers who adopt practices that exhibit poor type safety.'.
In (some) Java _programs_ you would circumvent modelling your domain correctly because it would be too cumbersome to use, therefore Java _programs_ tends to be less typesafe...
I suppose that is how I use the term and heard it used in my everyday life, e.g. 'this code is more typesafe than that' and so on and so on...
I guess I should have made it more clear that this how I used the word 'typesafe' (though your definition of it is more correct) and underlined that I meant Scala/Java _programs_ not the language (nor the type system) itself.
Still, it is correct that type inference (rather obviously) doesn't have anything to do with type systems.
In their list of risks there's one missing: it makes changing the return type of a method to a subclass a subtly breaking change. Say some api defines a method,
Collection<String> getNames() { ... }
and in your code you do
var names = getNames();
if (...)
names = Collections.emptySet();
Now, if the api later changes getNames() to be
List<String> getNames() { ... }
your code breaks. That's one of the advantages of only allowing this on final variables (besides reusing an existing keyword): it means this can't happen. Of course you already have a problem if the class has subclasses but I would argue that this is more subtle and problematic.
It will be caught as a compiler error. Since you would typically have the method signature declare that it returns the most general type it should be a non-issue. It's really no different from breaking code today by changing a method signature in an API.
It's possible that it's more a theoretical than a practical problem, I would hope so. But it does seem to me like fundamentally a problematic property of a type system that having more accurate types can break your program. It's the static type equivalent of breaking the Liskov substitution principle.
If a library author makes such a fundamental change their public API by making a method return more specific, I might actually want to know about it. I don't find it particularly problematic since it seems like code smell for that sort of change to happen in the first place.
This is pretty nice. Hope it goes through. It's about time that we had local-variable inference in Java. It does mean that the diamond can't be used this way:
var list = new ArrayList<>();
There is no way to infer the type of the generic parameter. So we will go back to doing:
var list = new ArrayList<String>();
Which isn't a big deal IMO because the generic parameters had to be specified on the LHS anyway to use the diamond. You also end up using fewer characters in general with var even without being able to use the diamond.
Regarding val vs let, I prefer let simply because it's harder to accidentally type let vs typing var instead of val (or vice versa).
val is a much better idea. Final is longer to type, in many cases as long as just declaring the type. We should be encouraging immutability not discouraging it by making it more verbose than necessary.
As far as val vs let goes, I actually prefer val, because it has symmetry with var. That is to say "variable foo" is more similar to "value foo" than "let foo," since "value" is an adjective like "variable". Of course, I suppose this is personal preference.
there is but I don't know if it can be retrofitted onto java.
In rust and haskell, ect. types can be worked out backwards so that the type is figure out with use.
fn main() {
let mut v = Vec::new();
v.push("hello");
v.push("world!");
println!("output: {:?}", v.join(" "));
}
the type of v is `Vec<&str>` worked out backwards from where &str literal is added(since the type of push is fn `push(&mut self, value: T)` the T generic of Vec<T> must be &str).
I hope this gets rejected. The given examples are too ideal as they are all using familiar Java standard APIs. I doubt if it's a good idea in practice since you can end up with unreadable code when integrating with third party APIs.
Many would say List<String> in (1) is redundant but I would argue otherwise. The intention of List<String> in (1) is to reference to the underline ArrayList through List interface. (1) is trying to encourage programming to an interface while (2) just completely destroys the practice by inferring the type for list variable to be ArrayList<String>. That means subsequent method invocations after (2) can be methods from ArrayList not List.
If you can explain the value of restricting a local initialized with a new instance being inferred to be an interface type, you'll have a point. You won't be able to, because there is literally no point since the constructor must be statically referenced anyway, but I'd love to see you try.
Have you never Map<Shard, ListenableFuture<RpcEndpointResult>>? And then mapped it through various asynchronous transformations? Good god, the type soup.
There is a point where typing becomes verbose and decreases readability. If the compiler can infer, it should. It'll save time, money, and sanity.
You fail to see that type inference is optional. So yes for readability's sake, in your first example I wouldn't omit the "int", but in simpler cases you CAN omit if you want. And that's just nice, end of story.
What are the bad practices being promoted by this? How does it qualify as dumbed down? Simply ranting about things that, charitably, you don't seem to understand, does not make the rants valid.
As the sibling comment indicates, and I wish to amplify with precision, if you are using the static constructor reference to initialize the local, declaring the local reference to be of a super type is cargo cult nonsense, not a best practice. The concrete type is already locally known, nothing is gained via the abstraction.
This is complete nonsense if you're using a static constructor and not a factory. There's zero point to doing this for a local, statically constructed type. It's not a best practice.
If you were using a factory, var would infer the factory signature's type, which would normally be the interface.
No, it's not a complete nonsense, no. People abusing Java with overengineering and using factories and other enterprisey design patterns for everything made a lot of people leave the Java land!
> People abusing Java with overengineering and using factories and other enterprisey design patterns for everything made a lot of people leave the Java land!
Actually, people left Java land because they couldn't express what they needed to without using complicated design patterns.
Read my other reply to you on the subject! The API might be a small syntactic change, but the concepts and the implementation behind those for noobs are not. They learn OOP and constructors and then you tell them - they suck, use factories via static methods.
I agree. "var" as proposed is dumbing down Java. If there's an intelligent way to infer the interface, not the implementation, then I'd accept it, but otherwise, it's the JavaScriptification if Java given most IDEs can autocomplete this and let you correct if they assume wrongly.
The approach taken to preserve backwards compatibility with existing code that already uses `var` as an identifier is interesting:
"The identifier var will not be made into a keyword; instead it will be a reserved type name. This means that code that uses var as a variable, method, or package name will not be affected; code that uses var as a class or interface name will be affected (but these names violate the naming conventions.)"
I didn't know there were such things as "reserved type names", but I'm guessing `var` will be treated sort of like `String` or `Object` (always available without an explicit import) and maybe it'll be a class in `java.lang`?
Seems like a much "nicer" approach than when Enum types were introduced with the `enum` keyword, but I guess that was slightly different.
PHP recently did the same thing with PHP 7, which introduced type declarations for the scalar types. This meant we needed to reserve `int`, `float`, `string` and `bool`. To minimise backwards-compatibility breakage, we only reserved these as class/interface/trait names, and not in other contexts.
And it seems we were right to reduce the scope of it. PHP 7 prohibiting "String" as a class name ended up being a significant BC break affecting several projects. I imagine it might have been worse still if `string` was a reserved word.
For local variables within the body of a method it doesn't really matter. As long as the method signature is expressed in interfaces, you should be able to use whatever implementations you want within the method body.
Except when you add final, then there is little reason to have the less specific type.
But i see where you come from, it feels a bit sloppy to not document the intention that the variable _could_ be any kind of list, even though it happens to be an ArrayList. Not "this may lead to nasty bugs"-sloppy, but a slight case of "this won't win any beauty contests"-sloppy.
This is for local variables, though. You can use var as the return type of a method.
If your method bodies are so large that you need to be coding to interfaces within them (as opposed to just setting the return type of your method to the interface), the you've got other problems.
With all the fuzz behind Kotlin, just because JetBrains, I am surprised that Ceylon has so little awareness! Ceylon, which has great features, multiple compilation targets (can't wait for the LLVM backend), has a great entity backing it (Red Had), and is created by a celebrity (at least in the Java world) developer (Gavin King)!
Yeah, and it seems like a good project, but some downsides are that it has to support multiple compilation targets (which can split a language into multiple ecosystems), has only one major user (Red Hat), and was created by Gavin King
Red Hat > JetBrains though - I doubt Kotlin has a much bigger user base. Plus, being created by Gavin King is good, not bad. Also, multiple compilation targets are considered a plus in other languages (like Dart) - why is this bad for Ceylon? Especially when it along with Java it does not rely on native libraries like most of the other languages like Python, Ruby, and Node.js - it can be very useful and you don't need projects such as GWT if you can compile Ceylon to JavaScript directly.
Kotlin's got more "noise" around it lately though, so I suspect adoption will be quicker. Also, GMC is bigger than Red Hat, and while Telegram is smaller for sure, it's got a decent amount of weight to it. Both use Kotlin (at least according to the Kotlin homepage).
Gavin King is famous for three things in the Java world: creating the leaky-abstraction-riddled ORM Hibernate that never quite behaves the way you want, creating the never-really-used DI lib Seam that is notorious for being difficult to test and configure (despite standardized "javax" APIs), and being a difficult personality to work with.
Multiple compilation targets are a downside when:
- It means that as a user of the language I have to write different code because the collections/scoping on one target behave much differently than on another
- It means that as a maintainer of the language, I have to try to make core language features work with multiple very-different compilation targets, making the codebase N times harder to maintain, and usually meaning either that the language stagnates for long periods or that one platform is treated as "primary" and the others are second-class citizens that trail behind (see Scala on the JVM vs Scala.js, Clojure vs Clojurescript, F# vs Websharper, etc).
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 :)
> 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.
> 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.
My 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).
> 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
I 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).
Yeah, when the effort put into inserting types is higher cost than reading them (as with short lived / unstablized code), what you're saying makes sense.
Which is kind of why I wish for tooling outside the compiler to make the effort in specifying types (when it increases readability, maybe based on heuristics) very minimal. Wishful thinking.
Well, the warning from ghc gives you the inferred type, which can be helpful. Copying it in place blindly is not advised - it won't break, but something more specific might be better. And of course the compiler has no way of getting inferring what choice of type aliases will be most (correctly) communicative.
The proposal only applies to local variables with non-null initializers. I'm pretty sure you can't autowire local variables with Spring, and they don't have initializers anyway.
Yeah, but that's a case where "var" is probably the wrong choice anyways, because of the non-obvious "human type resolution" -- i.e. if I'm a human reading this code, I can't obviously tell the type FooDao, where in simple cases like var path = Paths.get("/foo/bar/bonk") I can.
var isn't a dynamic type. var is a compiler-inferred static type. `var fooDao;` will refuse to compile because the compiler can't infer the type. `var fooDao = new FooDao()` will still have the type FooDao for fooDao, it is just inferred by the compiler instead of being explicitly declared by the programmer.
That's because C++ combines type inference with a gnarly compilation process. In a language like Java, editors will support it and you'll just be able to "mouse over" (or press a key combo in Vim/Emacs) and it'll tell you the inferred type.
I wonder if this JEP draw any inspiration of http://www.eclipse.org/xtend/. For those new to xtend, think of it as xtend is to java like coffeescript is to javascript.
I wish most of the xtend enhancements were integrated to java asap!
The reaction here is indicative of why I'm starting to consider whether maybe it's best that Java stays Java. Sure, I'd personally be really annoyed typing super redundant type declarations everywhere. But then again, I wouldn't have to, because I'd simply use Scala, because it's more expressive. But a lot of people clearly love good old verbose Java.
Java has been super slow to adopt things that are more or less the norm in other popular languages. Why bother at this point? Isn't Kotlin a pretty good sugared-up Java, anyway?
Lightbend (caretakers of Scala, Play Framework, Akka, Spark, Slick, SBT, and others) wisely announced their dedication to both Scala and Java. It's caused a big stir in the community, but it kind of seems like that should make everybody happy. They're two ecosystems geared to serious software, built on a solid platform, but with very different constituencies. Lightbend realized there's no point in pressuring people to adopt Scala. People will do it on their own, if they want to. But with strong support of both languages, they can address a very broad swath of the development world.
The reaction from some reminds me that some Java programmers will always resist change. Java's current feature set is the one true way (tm) and any features other languages have are bad, that is until they are officially added to Java and are then suddenly considered part of the one true way (tm) and further evidence of the language designers' wisdom.
I hope they will give it second though. In Java 'var' or 'val' are not reserved keywords. It was difficult to migrate existing code when 'enum' was introduced. 'const' is reserved but nothing for variable, perhaps 'new' or some symbols.
I really want this. I gave up on Ocaml because it just isn't a big enough ecosystem but everything else seems like I'm making the wrong things explicit in the code.
Higher Kinded Types, yes, but implicits... Yuck. Scala has definitely proven them as a failed experiment, I hope we will never seem them again in languages going forward.
Implicits are tricky, but they are the best way I know of to solve the problems they do. Other languages (JavaScript, Swift, etc.) have essentially open classes, where attachment of additional functionality to a type impacts a program globally. Implicits make it much easier to scope these extensions.
They are also quite useful for wiring up context (take Akka's ActorSystem, for example) at the declaration level, so that the bodies of your classes and functions only explicitly talk about your domain objects, rather than framework machinery.
They're also a big part of the power behind projects like Shapeless, which are quite useful for scrapping boilerplate.
Most controversially, they are also used to implement implicit type conversions. Modern idiomatic Scala discourages this, but it can sometimes be helpful.
I really like Scala. It is a really interesting language, and a productive one for me to program in. However, I agree that implicits are a bad feature. 90% of the confusing bits of Scala are because implicits exist and are, well, implicit.
I'd find var and val way too easy to confuse. Added to the fact that Java has "effectively final" variables already there should be no need to distinguish here. If anything I'd probably be in favour of "var" and "final var", which would follow existing syntax in the language.
Yeah, let's spread the nonsense further! There are constants and variables; values are something else. Just because "val" is a short abbreviation everybody gets, it doesn't mean we should abuse it!
Maybe i'm just weird, but to me, when i hear the word "constant" i expect something that is nailed down at compile time.
final int chosenByFairDiceRoll = 4; // a constant
final long millis = System.currentTimeMillis(); // not a constant
When you say "values are something else", is it that you would consider chosenByFairDiceRoll a value, but not millis? I guess that you might have had more exposure to languages that use "const" instead of "final" than me...
I disagree with that sentiment. Constant to me implies decided at compile time. The reason we should use "val" is because it is a short abbreviation everybody gets. It also has symmetry with "var," as "value" and "variable" are both adjectives, whereas "let" is a verb.
I've been programming for 15 years and I'd have no clue what the difference between val and var is without looking it up. So I guess your definition of 'everybody' is JavaScript programmers?
And every time I see 'var', I cringe because I don't know if they mean variable or variant. But 'val' is just usless. Variables evaluate to values, constants evaluate to values, expressions are values, first-class functions are values... Everything is val.
Java loses its explicitness with each release. And it's still much worse as a language than Scala, Kotlin, Groovy, Ceylon (and probably always will be behind). I don't see much value in those improvements. If one wants a better Java, he can use it right now. What Java needs is JVM improvements, like value types or full support for bytecode hot reload. Java 1.4 was very minimalistic and beautiful language. I liked it that way.
Who's typing Java code all by hand these days, though? There various IDEs that save me all the typing.
It's also nice to be able to go the declaration of a variable to see exactly what type it is - that's mostly for code not written by myself, or old code that I do not remember. For local variable that's fine (I hope we won't infer types this way for method parameters), so I guess I like it there.
> Who's typing Java code all by hand these days, though?
> There various IDEs that save me all the typing.
Some truth in that, it should be safe enough to assume that the lightweight editor crowd has long self-selected out of java. I often do write-time type inference by declaring anything as an int and then, at the end of the line, pressing whatever magic key that makes the IDE rewrite it to the actual type so that it compiles.
One of the syntax options in the JEP is "final name = RHS;". When i first saw that i could not understand why anybody in their right mind would even consider a partial solution like that. But now that i wrote about write-time type inference, it starts getting more and more attractive:
A tiny nudge towards immutability, natural "write-time type inference" that is correct until the first nonfinal access is written (IDEs already offer a "make nonfinal?" fix in that case) and nonfinal identifiers will often be a broader type than the RHS assignment anyway.
>>I hope we won't infer types this way for method parameters
No, it doesn't. From TFA: "This treatment would be restricted to local variables with initializers, indexes in the enhanced for-loop, and locals declared in a traditional for-loop; it would not be available for method formals, constructor formals, method return types, fields, catch formals, or any other kind of variable declaration."
Which is pretty much how C# does it.
I gather that it's common for functional languages to infer pretty much everything for you most of the time, which I think would actually be nice. But presumably that's either difficult or impossible in Java.
Being conservative about what you add to the language is a feature, not a bug. Not all languages need to be on the cutting edge (even if I personally prefer the languages that are)
See Brian Goetz's talk on language stewardship (https://www.youtube.com/watch?v=2y5Pv4yN0b0). I'm sure nobody wants to add new and interesting features to Java more than the people who work on the language every damn day, but they've made a strong commitment to the community to not break existing code.
C# shows pretty compellingly that adding features does not have to break existing code. For example, you can use every keyword added after 1.0 as identifier, too.
But it can make things more complex too. e.g. I recently learned that the main reason that asynchronous C# methods have to be declared "async" is so that the compiler knows that in the method body, "await" is a keyword. Without this backward compatibility requirement, it could be inferred from the return type + the presence of 1 or more "await" keywords.
The website isn't the prettiest but Lombok is a fantastic addition to a Java codebase and massively reduces the amount of boiler plate often associated with Java. A list of Lombok features https://projectlombok.org/features/index.html
Java 8 + Lombok + Guava == happy developers (well at least in my case :) ).