LJC Lambdas Hackday
Brian Goetz
brian.goetz at oracle.com
Tue May 29 07:11:18 PDT 2012
Thanks for the effort and the writeup! This has been extremely helpful
for us!
Some comments:
> Bugs
>
> 1. Target typing for diamond operators being passed into methods
> doesn’t seem to work quite right at the moment.
I wish we could consider this a simple bug, but as Maurizio points out,
this is a pretty big leap from local type inference towards more global
type inference. We're evaluating our options here, but if we could have
fixed this already, we would have :(
> 2. The state of the lambda collections libraries document lists
> several methods that haven’t been implemented yet, for example sum()
> and mapBy(). On a side note - sum() seems like an interesting method,
> what is its type?
And now you've discovered the reason why it is not yet implemented :)
Our story for primitives is still being mapped out, and there are a
number of options here. If we choose to go in the direction of
specializing streams for the common primitives (Int/Long/DoubleStream),
then the sum() method would live there and its type would be obvious.
Another alternative is to fuse mapping and reducing into a single
operations, and have a sumBy(IntMapper<T>) operation.
> 1. People thinking that map/reduce return an eagerly evaluated List,
> rather than a lazily evaluated Iterable. This was especially confusing
> when we discussed the sorted method, and whether calling getFirst() on
> a lazy iterator ends up generating any iterators that you’ve
> dependently chained. A brief scan of the source shows you’re using a
> priority queue, but this is the kind of thing that people will get
> confused at when you’re now talking about a lot of their collections
> operations being lazy.
Yes, this is one of the key education points we need to address.
Laziness can be surprising, and the sorted() method is among the
weirdest (it is lazy in the sense that no computation happens when you
call sorted(), but eager in the sense that much or all of computation
has already happened by the time the first element comes out.)
We've come to the conclusion that putting these methods on Iterable<T>
makes the confusion surrounding laziness worse, given that most
Iterables (though not all) are backed by data structures and this colors
our thinking. Instead, the Stream abstraction should behave more like
an Iterator, and I think this will help a lot -- if filter returns a
Stream, the idea that it will be lazily computed comes more readily to
mind.
> 2. Some programmers appear to have basically no functional programming
> experience and were confused as to why they couldn’t assign back to
> their parameter in a map. For example they wanted to write:
>
> someIntegers.forEach(x -> x = x + 1)
>
> rather than:
>
> someIntegers.map(x -> x + 1)
>
> I know this might sound silly to people subscribed to lambda-dev, but
> getting people out of a solely imperative mindset of one of the
> adoption challenges that is faced by this JSR. If I may be so bold as
> to make a suggestion in order to help people in the right direction:
> lambda arguments being default final. (Or maybe even always final)
This is worth considering. Sadly it would be an assymmetry with inner
classes (we are extending the treatment of effectively final to inner
classes, and generally trying to reduce gratuitous differences.)
> 3. Expressions that evaluate to void vs code blocks.
>
> “someStrings.forEach(s -> System.out.println(s));”
>
> doesn’t compile because the function call is an expression that
> evaluates to void, so it's incompatible with Block<String>.
>
> “someStrings.forEach(s -> { System.out.println(s); });”
>
> works fine because the braces with no return allow the user to
> convince javac that it's returning a void.
>
> Two different groups of 4 people and at least one of the instructors
> found this quite confusing. In many functional languages, with the
> equivalent of void (Unit, etc) being a normal type, the first
> statement would have type checked.
Yes, we've been worried about this as well. The nice division between
expression lambdas and statement lambdas is clean, and that cleanliness
makes it possible to make better decisions about things like overload
selection. However, as you point out, it may well just be too rigid for
most people's conception of what void means. We're experimenting with
some alternatives.
> Opinions
>
> These are opinions that I sourced from people whilst they were using
> the lambdas build. All of them were supported by at least 2 people
> and many of them were common threads when talking to different groups
> of programmers.
>
> 1. Javadoc needs examples. For a lot of Java developers they’ll have
> a problem and need to figure out which higher order function is
> appropriate for their use case. For example they have a list of
> numbers that they wish to increment, but they’ll need to figure out
> whether they want to use map or reduce. Again - seems trivial to
> people with some experience but many won’t have that experience. In
> this case the style of Javadoc - ie explain what’s happening - that’s
> throughout Collections isn’t necessarily that appropriate. It would
> be much more helpful to have a simple code example in order for people
> to grok the pattern that they need to apply. E.g. Incrementing
> numbers for map, concatenating a list of strings for reduce etc. It
> would also help people coming from other backgrounds if methods listed
> similar method names.
Absolutely. We made the deliberate choice to invest less in Javadoc
while the API is in flux, to allow for more rapid turns, but I agree it
is an impediment to understanding.
> 2. Complex types are hard to read and understand. Scala already gets
> a really bad rep for this, and Java will as well if you’re not
> careful. Consider the function map’s signature:
>
> public static<T, U> Iterable<U> map(final Iterable<? extends T>
> iterable, final Mapper<? super T, ? extends U> mapper)
>
> There are a couple of things that you can do on a practical level to
> help people understand this better.
>
> a. Write Javadoc in a way that discourages people from reading the
> types in order to figure out what’s going on. So, if the javadoc
> explains what a mapper is, and what the Iterable<U> is that’s returned
> then you don’t need to understand the types at all.
>
> b. Use sensible type parameter names. The basic fact is that generic
> parameters are variables - they simply range over sets of types,
> rather than sets of values - so why not apply descriptive rules for
> variable names as you would normal variables. For example:
>
> public static<From, To> Iterable<To> map(final Iterable<? extends
> From> iterable, final Mapper<? super From, ? extends To> mapper)
>
> Now you don’t get people looking at the type signature and immediately
> asking themselves - what does ‘U’ mean?
Agreed. I always felt that the "one letter rule" was borne of a
delusion that type parameters would only be used in obvious ways, such
as the elements of a collection. But we passed that long ago, and we
should acknowledge it.
The wildcards don't help either :(
> When trying to lambda-ise cassandra we ran into several Apache Ant
> bugs that we established temporary workarounds for. I think before we
> run an event like this again we’ll want to patch Ant so that it works
> ok. Do you guys have internal builds of ant that work ok with Java 8?
> If you don’t, I’m sure we’re more than capable of fixing the problem,
> but its still easier if we don’t have to!
We haven't run into these issues. I use a relatively old version of ANT
for running tests and haven't seen these yet.
Thanks again! Great input.
More information about the lambda-dev
mailing list