Reader Mail Bag -- Local Type Inference (JEP 286)
Brian Goetz
brian.goetz at oracle.com
Fri Mar 25 19:24:56 UTC 2016
(Please do not respond directly to this mail; if you want to respond,
please use the follow-up survey.)
As promised yesterday, I'm back to cover some of the more subjective
responses and questions to the survey.
Overall, the responses were strongly in favor of the feature; given a
choice of great/ok/bad idea, 74% chose "great idea", 12% chose "OK", and
10% chose "bad idea". The written comments had more of a negative bias,
but this shouldn't be surprising -- people are generally more inclined
to speak up in disagreement than in agreement. The positive comments
were very positive; the negative comments were very negative. So no
matter what we do here, some people are going to be very disappointed.
(One interesting observation here is that, while developers frequently
complain about Java's verbosity, there is at least a silent minority
that *likes* that verbosity, and complains only when we threaten to take
some of it away.)
Readability
===========
The biggest category of negative comments regarded concerns for
readability. We have always held out "Reading code is more important
than writing code" as a core value of the language; plenty of folks
threw this in our face, assuming that this feature would inevitably lead
to less readable code.
For example, code like the following strawman was offered by several
commenters:
var x = y.getFoo()
and offered as evidence of "see, its unreadable, you have no idea what
'x' is." The readability problem here, though, stems primarily from the
fact that 'x' is a silly variable name.
While every situation may be different, we believe that, with judicious
use of this feature in well-written code, readability can actually be
*enhanced*.
Consider a block of locals:
UserModelHandle userDB = broker.findUserDB();
List<User> users = db.getUsers();
Map<User, Address> addressesByUser = db.getAddresses();
What is the most important thing on each line? IMO, in a block like the
above, the variable *names* are more important than the *types* or their
initializing expressions -- they describe the role of the variable in
the current program. And note that the variables names are not so easy
to visually pick out from the above code -- they're stuck in the middle
of each line, and at a different place on each line.
Ideally, we'd like for the most important thing to be front-and-center
in the reader's view. (Some languages have us declare variables with
the syntax "name : type", which also furthers this goal, but that ship
sailed in 1995.)
If we rewrite the above block with inferred types:
var userDB = broker.findUserDB();
var users = db.getUsers();
var addressesByUser = db.getAddresses();
the true intent of this code pops out much more readily. The types are
not needed, because we've chosen good variable names.
Another aspect in which this feature could improve readability (as
suggested in the JEP) is that users frequently construct complex nested
and chained expressions not because this is the most readable way to
write the code, but because the overhead of declaring additional
temporary variable seems burdensome. (We all do this.) By reducing
this overhead, implementation patterns will likely reequilibrate to a
less artificially-condensed form, enhancing readability.
A second version of this complaint could be summarized as "bad
programmers will use this as an excuse to write bad code." (This is
sometimes phrased as some form of "I like this feature for me, but am
not sure other developers can handle the responsibility.")
This may be true, but I think we all know that bad programmers don't
need excuses to write bad code.
There is always a balance to be found between "preventing bad things"
and "enabling good things", and reasonable people will reasonably differ
on which is more important in any given situation or timeframe.
Ultimately, this comes down to the usual question of "Here's a feature
that can be used for both good and bad; should we trust developers to
use it responsibility, or take that choice away from them?" (And of
course, proponents on both sides can find precedent in past decisions to
claim "the spirit of Java" is on their side.) Recall too that we heard
all the same arguments about lambdas -- that they were too advanced,
that Java programmers couldn't handle them, that they'd destroy
readability (and Java with it) -- in the not-so-distant past.
Further, just because the feature exists, doesn't mean that we can't
have stylistic guidelines regarding its use -- just like any other
feature. (Users can write 1000-line long methods, or pick terrible
variable names, or store all their state in public static variables, but
they shouldn't.)
Goals
=====
Many people made assumptions about the goals, such as "this is about
concision", or "this is about making writing code easier." But these
assumptions are incorrect. The goal here is about *reducing ceremony*
-- allowing irrelevant details to be elided where appropriate. In many
cases, manifest types are not only inconvenient to write, but they get
in the way of easily seeing what is going on when reading code.
Note that this feature is, quite deliberately, restricted to *pure
implementation details*. We don't use inference to determine the types
of fields or methods; we are willing to accept a higher degree of
ceremonial overhead for interface contracts than for implementation
details. And the body of a method is an implementation detail.
Several users simply expressed the notion that "Java is just fine as it
is". Others simply asked "Why?"
Proponents on both sides claimed "simplicity" was on their side :)
First-hand experiences
======================
It's not surprising that many people were quicker to see the bad things
that such a feature might enable than the good things; this is human
nature. But we are inclined to take the comments from those who have
used this before in other languages most seriously, as they are the most
informed about the tradeoffs.
Of the respondents who cited first-hand experience, several listed this
as a major friction point of polyglot projects, or of moving to Java
from C#, C++, or Groovy (though several did point out that blanket use
of inferred types everywhere was inadvisable, and that it required some
judgment to use it correctly.)
More information about the platform-jep-discuss
mailing list