The Artima Developer Community
Sponsored Link

Java Community News
Elliotte Rusty Harold on Type Inference for Java, and on Moving On

36 replies on 3 pages. Most recent reply: Apr 27, 2007 7:26 AM by Isaac Gouy

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 36 replies on 3 pages [ « | 1 2 3 | » ]
Alan Keefer

Posts: 29
Nickname: akeefer
Registered: Feb, 2007

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 23, 2007 1:18 PM
Reply to this message Reply
Advertisement
I have to say that I really don't understand the resistance to type inference in Java; I can't imagine that anyone who's ever used a strongly-typed language with type inference would really want to go back to one without it. And talk about things like overloading the semantics of "final" so that you can only use type inference with final variables is, frankly, mind-boggling.

An IDE can help, but only so much. I love the fact that a good IDE (I use IntelliJ) can make Java more palatable, but I don't like having to change my work habits to fit in with the tooling. If I want to create a variable that's the result of a method call that returns some generic, I have to work backwards: write the function, then use the "assign to variable" refactoring to create the variable. Otherwise, I end up typing out Map<String, Class<? extends Foo>> all the time. If I want to change Map<String, Class> to Map<String, Class<? extends Foo>>, I either have to use the IDE's refactoring tools or I have to do a whole lot of typing. Having to lean on refactoring tools to do simple operations on local variables seriously interrupts my work habits, and anything that requires that level of tooling is clearly broken. Refactoring a method that's called across twenty classes? Tools are great. Changing the type of a single local variable with only one declaration? I shouldn't have to use tools for that.

As to readability, 90% of the time (maybe more) I'd say that a variable's type is obvious from the assignment. var x = new HashMap<String, String>() is pretty obvious. So is var x = 1 + y, or var x = Collections.singletonList(y), or var x = isSomeConditionTrue(). If the assignment isn't obvious, where the variable is used will often make it obvious. In the few cases where it's still not obvious, you still have the option to declare the type explicitly, and usually clicking into a few method calls is enough to tell you what you need. And a decent IDE ought to be able to tell you the inferred type quickly too. The point is that explicit types optimize for the 5-10% of the time where it's not painfully obvious what the type in, and the language should be optimizing for the common case instead.

It's not just about saving typing, though; it's also somewhat about encapsulation. Type inference allows you to not care about types in contexts where what matters is that the types match up right, rather than what the types acually are. Take var x = foo(); bar(x);. What's important is that the return type of foo matches the argument that bar takes; as far as the caller is concerned, it probably doesn't matter if x is a String or some custom Object type or what. Sure, when writing code you need to know what you're doing, but if foo and bar change in compatible ways, why do I have to modify the code that calls them if the calls will all still work? A lot of the value of type safety comes from the fact that it catches errors, and type inference will still cause all those errors to be caught, with less work (and less thinking) on the part of the programmer. It makes the code cleaner in a lot of ways by encapsulating the type; as long as everything is still type-compatible, you can refactor without changing the calling code. Tools help with those sorts of refactoring, but which would you rather do: change a library and modify 150 source files that call the library, or change the library and modify 5 of the source files because the other 145 still work because of type inference?

To put it more succintly, it's not just about how many characters I have to type (and read); that's important, but conceptual clarity is another benefit here. The more things I don't have to focus on or care about in my code, the more attention I can pay to the things that do matter.

Abraham Tehrani

Posts: 5
Nickname: atehrani
Registered: Mar, 2007

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 23, 2007 3:16 PM
Reply to this message Reply
Type Inference is evil due to it's ambigiouty

auto map = new HashMap();

What is map inferred to be? HashMap? Or Map? We have been taught to not tie ourselves to concrete classes. So map can be inferred in three ways

Map map = new HashMap(); // <-- This should be preferred
HashMap map = new HashMap();
AbstractMap map = new HashMap();

How will the type be inferred?

It also ruins readability, what type does this method return?

auto foo = obj.getFoo();

I have no idea, this makes code reviews that much more difficult.

Lastly what is type inference solving for??? From what I have gathered it is so that developers don't have to type as much. Probably the worst reasoning one can come with for a language change IMO.

We pay too much attention on development and implemention, so much so at the cost of maintenance. Most senior software engineers will tell you that the majority of your cost will come from the maintenence cycle in the SDLC.

You must balance between short and long...

Carson Gross

Posts: 153
Nickname: cgross
Registered: Oct, 2006

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 23, 2007 3:36 PM
Reply to this message Reply
<i>We have been taught to not tie ourselves to concrete classes</i>

What we have been taught by our programming betters is wrong. Abstraction is useful across method invocation points, but within a method it is an exercise in academic back-patting. It's a HashMap. Get over it.

I don't know who this Alan Keefer guy is, but he's making a lot of sense to me.

Cheers,
Carson

Abraham Tehrani

Posts: 5
Nickname: atehrani
Registered: Mar, 2007

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 23, 2007 4:24 PM
Reply to this message Reply
Effective Java Item 16: Prefer interfaces to abstract classes and Item 17: Use interfaces only to define types

Given the choice of taking Josh Bloch word and yours, no offense, but I will take Josh's word anyday.

Besides information hiding also pertains to hiding type information.

public ArrayList<Customers> getCustomers();

public List<Customers> getCustomers();

The latter is preferred for (hopefully) obvious reasons.

My original point, was how is type inferred? Does it default to concrete classes (which is not always the "right" thing to do)? Or does it default to interfaces or abstract classes (or parent)?

Alan Keefer

Posts: 29
Nickname: akeefer
Registered: Feb, 2007

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 23, 2007 4:54 PM
Reply to this message Reply
It's far more important to have method return type and parameter types be interfaces than to have local variables be typed as interfaces. Within a class/method, you already know what type of class you instantiated, so there's no information hiding to be gained from declaring it as a concrete type anyway. At best it prevents you from binding your own code to a particular implementation, but that's about it, and in my experience it's pretty rare that I create a local variable as a HashMap and then invoke some HashMap-specific methods on it. So presuming you're still writing your methods to take/return Map instead of HashMap, having the type inferred to HashMap doesn't make any difference. Changing from constructing a HashMap to a FooMap won't lead to any code changes or compile errors. It might ruffle a few hard-line purists feathers to have the variable actually be typed as a "HashMap" instead of a "Map", but it doesn't actually have any practical significance, so I honestly don't think it's a legitimate concern.

At the end of the day, there's no reason an inference engine really needs to be that complicated or tricky. Almost all the use cases around around local variables or at most member variables, and those should never leak outside of the class or method anyway (unless you're making type-inferred member variables public instead of wrapping them in accessors, which would be pretty crazy).

I really think people get pretty worked up over relatively minor issues like this largely because it's different than waht they're used to, and I bet that if most people actually were able to make use of those features they'd instantly recognize their value.

Carson Gross

Posts: 153
Nickname: cgross
Registered: Oct, 2006

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 23, 2007 5:37 PM
Reply to this message Reply
Effective Java Item 16: Prefer interfaces to abstract classes and Item 17: Use interfaces only to define types

I agree with that item with respect to public method examples you give. It's with respect to local variables that I don't agree. Local variables are inherently implementation details and information hiding just ain't that useful, and it sure as heck ain't worth the syntactic baggage of declaring all your types twice, *especially* in the presence of generics.

In our internal language, we default to the concrete class. It's the right thing 99% of the time and, when its not, you have the option of declaring the type. Problem solved.

A gentleman's disagreement, then?

Cheers,
Carson

Daniel Jimenez

Posts: 40
Nickname: djimenez
Registered: Dec, 2004

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 24, 2007 7:43 AM
Reply to this message Reply
> It might ruffle a few hard-line purists
> feathers to have the variable actually be typed as a
> "HashMap" instead of a "Map", but it doesn't actually have
> any practical significance[.]
>

You mention significance just above:

> At best it prevents you
> from binding your own code to a particular implementation,
> but that's about it, and in my experience it's pretty rare
> that I create a local variable as a HashMap and then
> invoke some HashMap-specific methods on it.

I'll often change code from
new HashMap
to
new TreeMap
for debugging / performance purposes, and it's not my experience that invoking HashMap-specific methods is rare.

Considering the value of type inference solely against declaring types twice, I prefer syntax like
Map<KeyType, ValueType> map = new XxxMap<>();
(where the empty angle-brackets signify a copy of the generics defined on the declaration) - it decreases the repetitiveness of typing the generics declarations while allowing local variable declaration against interfaces.

There are other reasons type inference would be nice to have (eg,
var x = getFoo(); bar(x);
) but I'm not entirely convinced local variable duplication is one of them.

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 24, 2007 8:36 AM
Reply to this message Reply
>Take var x = foo(); bar(x)

Geez, that's the best we can do as a reason for adding type inference? Try this:

bar(foo());

Even less typing, automatic type inference, and no need to change the language.

Oh, you use x in a bunch of other places? Well, then maybe it should be declared cause it's important and a maintenance programmer may want to know what the heck it is.

Carson Gross

Posts: 153
Nickname: cgross
Registered: Oct, 2006

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 24, 2007 9:35 AM
Reply to this message Reply
Geez, that's the best we can do as a reason for adding type inference?

Pretty much. Keep in mind: we are stupid and/or malevolent.

As a side note, I usually inline all my local variables to the point that my methods consist of one enormously long line of code. A *manly* line of hundreds, nay *thousands* of characters, that smells of musk and grime, sending future maintainers of my code scurrying back to their consulting firms. Where, frankly, they can rot.



var userCache = new HashMap<Key, User>();

Map<Key, User> userCache = new HashMap<Key, User>();

[code]

You be the judge. Bonus points if you think about jacking a Class<? extends SomeClass> in there as well.

Cheers,
Carson

Carson Gross

Posts: 153
Nickname: cgross
Registered: Oct, 2006

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 24, 2007 9:42 AM
Reply to this message Reply
and it's not my experience that invoking HashMap-specific methods is rare.

Hashmap's methods form a proper subset of TreeMap's.

http://java.sun.com/j2se/1.4.2/docs/api/java/util/HashMap.html

http://java.sun.com/j2se/1.4.2/docs/api/java/util/TreeMap.html

Using a HashMap-specific method would be a trick.

Cheers,
Carson

Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 24, 2007 11:12 AM
Reply to this message Reply
> Geez, that's the best we can do as a reason for adding
> type inference?

>
> Pretty much. Keep in mind: we are stupid and/or
> malevolent.

Thanks for keeping a polite tone.

> var userCache = new HashMap<Key, User>();
>
> Map<Key, User> userCache = new HashMap<Key, User>();


Frankly, I prefer version 2. It's tidier. My eyes can scan down the left side of the code and quickly find that userCache is a Map<Key, User>. What's on the right side of the equals sign I tend to ignore. In fact, I'd write:

Map<Key, User> userCache = new HashMap();

Java does generate a brain-dead stupid and truly meaningless warning on this - if only you could turn that warning off it fixes much of the "we need Type Inference because it feels silly to type <Key, User> twice" issue. I agree with you, typing <Key, User> twice is silly.

Is there an easy way to turn off that warning BTW?

Jeff Ratcliff

Posts: 242
Nickname: jr1
Registered: Feb, 2006

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 24, 2007 1:36 PM
Reply to this message Reply
> Effective Java Item 16: Prefer interfaces to abstract
> classes and Item 17: Use interfaces only to define
> types

>
> I agree with that item with respect to public method
> examples you give. It's with respect to local variables
> that I don't agree. Local variables are inherently
> implementation details and information hiding just ain't
> that useful, and it sure as heck ain't worth the syntactic
> baggage of declaring all your types twice, *especially* in
> the presence of generics.
>
> In our internal language, we default to the concrete
> class. It's the right thing 99% of the time and, when its
> not, you have the option of declaring the type. Problem
> solved.

This is why I'm not a big believer in rule lists. They almost always oversimplify or they are based on the author's limited experience in the broader world of software development.

The only universal rule I can think of is "do what is appropriate in the context of your project", but it's hard to sell books on a rule like that.

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 24, 2007 7:14 PM
Reply to this message Reply
> auto map = new HashMap();
>
> What is map inferred to be? HashMap? Or Map? ... map
> can be inferred in three ways

According to the article that the original post linked to, map gets inferred as MashMap.

I have wondered whether this belongs in an object-oriented language because it's common to manipulate objects through their base classes. In fact, in OCaml, there are special math operators for handling floats so that literal numbers will be inferred correctly (+. -. *. and /. instead of + - * /). I get hung up on this largely because I try to combine it with the idea of object literals. Given a particular literal (say, {4, "hello"}) how can I infer the correct class for that object? Especially if two unrelated classes happen to have an integer and a string for their only data fields?

Turns out the answer is that the type inferer does not know which one, and must ask. In fact, this is beside the point, because this level of type inference is well beyond what's being looked at for Java. It's not hard to implement; just take your type checker and turn it inside out. Instead of asking "is the type on the left hand side compatible with the type on the right hand side?" find out what the "one correct type" for the right hand side is, and that's the type for the left hand side.

Java's proposed type inference (and C++'s proposal) is simply a shorthand for "in this particular case, I don't care what type x is, just pick a compatible type." I don't see that causing maintenance nightmares. The difference between this level of type inference and the level of type inference you see in OCaml is at least as big as the difference between C++ templates and JJava/C# generics (or Java's type system and SQL's type system).

Isaac Gouy

Posts: 527
Nickname: igouy
Registered: Jul, 2003

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 25, 2007 8:52 AM
Reply to this message Reply
Max Lybbert wrote

> I have wondered whether this belongs in an object-oriented
> language because it's common to manipulate objects through
> their base classes. In fact, in OCaml, there are special
> math operators for handling floats so that literal numbers
> will be inferred correctly (+. -. *. and /. instead of + -
> * /).

Are there special math operators for handling floats in SML or Haskell or Clean?

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

Re: Elliotte Rusty Harold on Type Inference for Java, and on Moving On Posted: Apr 25, 2007 2:55 PM
Reply to this message Reply
> > In fact, in OCaml, there are special math operators for
> > handling floats so that literal numbers will be inferred
> > correctly (+. -. *. and /. instead of + - * /).

I have to admit I made a mistake. Even C is able to "infer" that 2.0 is a float. C++ officially permits (but, IIRC, does not guarantee) the compiler to recognize that a particular numeric constant is too large to fit into an int, but small enough to fit into a long. Inferring the type of a numeric literal is easy. Inferring the type of a variable is much harder. Given a = b + 2.0, what are the types for a and b?

I thought OCaml solved this by requiring these special math operators, but it looks more like OCaml just doesn't allow overloaded operators (or, at least, not for this). + adds two ints together, so x + 2.0 is incorrectly typed. You either need x + int_of_float 2.0 (or whatever the correct syntax is) -- in which case x is an int -- or x +. 2.0 -- in which case x is a float. See http://www.ocaml-tutorial.org/the_basics#Defining_a_function .

> Are there special math operators for handling floats in
> SML or Haskell or Clean?

SML appears to understand operator overloading sufficiently to see a difference between int + int and real + real.

Haskell doesn't require these "special" math operators. But Haskell, IIRC, only has one numeric datatype visible to the programmer: an arbitrary-length, arbitrary-precision datatype (I'm sure internally that "small" whole numbers are stored as ints, and "small" real numbers are stored as floats, but everything acts like a BigNum class of some kind).

Clean also supports overloading, and does not require these other operators.

So, it appears that the operators aren't for the type inference engine, but rather are for the human programmers.

Flat View: This topic has 36 replies on 3 pages [ « | 1  2  3 | » ]
Topic: Cay Horstmann on the Meaning of Return Previous Topic   Next Topic Topic: Scott Ambler on Agile Myths


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2017 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us