[The Java Posse] "The closure debate is pants" - No, no it isn't.

3 views
Skip to first unread message

Reinier Zwitserloot

unread,
May 25, 2010, 10:24:28 AM5/25/10
to The Java Posse
I got the impression from Dick's note on the closure debate, as well
as the response from the black hat guy, that there's some confusion
about the closure debates. Yes, that's plural.

There's the: "Should Java have closures" debate. This debate is
basically over. Mark Reinhold wants it, and convinced enough people
within sun (before the oracle take-over). I doubt this will be back-
pedalled now.

There's also the: What should it LOOK LIKE debate. This is a complex
debate that hits on a gigantic amount of issues, simply because there
are a bajillion ways to fit the following requirement set, and not one
of them is the obvious right answer:

1) Make it simple to write block-like constructs in java (simpler
than it is now, at any rate)
2) Make sure whatever construct you come up with makes Parallel
Arrays nice too (required use case)
3) Make sure whatever syntax you come up with is invalid if compiled
with javac 1.6, and that anything written for javac 1.6 does not
change in meaning. (backwards compatibility)

There are a bunch of issues which simply have no clear answer, so the
debate on all of these is long, complicated, and involves massive
introspection of existing java code as well as example "future" java
code to see which makes for the better choice. The list is pretty much
endless, so I'll just raise some of the major ones:

1) The 'strawman' of Reinhold at Devoxx allowed something like
"Closure foo = whatever; foo();" however, in java, unlike just about
every other language with closures, methods and variables are separate
namespaces. The above is mixing and matching them; "foo();" currently
means: Invoke a method named 'foo'. It does not mean: Do something to
a variable named foo. Should we break the separate namespace rule to
make closures look more natural (but with a bevy of java puzzlers for
when you have methods named foo as well as closure vars named foo -
especially because of backwards compatibility), or should we move away
form the strawman and use for example foo.invoke() or some other
operator such as foo#() to 'run' a closure? Anyone who thinks there's
a clear right answer to this is delusional. In practice there's an
unclear right answer which is to move away from the strawman, as the
effects of making 'someClosure();' work are quite large, and this is
in fact the current status quo. The specific syntax for now is: "foo.
();"

2) What should they look like? The strawman seems clear enough but
has its problems when you nest closure types in closure types (param
type of a closure is itself a closure) especially if some of the
involved closure types throw exceptions. There's also some risk that 1
minor typo results in a baffling error message, as closure type
declarations and closure block declarations look very very similar in
the strawman. A lot of the discussion back in the BGGA / FCM / CICE
days was in fact all about what it looks like, e.g. "I don't like the
=> thing in BGGA".

3) How should it be implemented? Reifying the closure types is
virtually impossible, as any closure proposal ought to work well with
generics, which aren't reified, and there's no room in the agenda to
try to reify generics. But without reified closure types, having
arrays-of-closures becomes pretty much impossible, and you get the
same erasure problems that generics have. There's some discussion on
whether or not a form of reification can be added, though the
conclusion so far seems to be: No. However, if reification isn't
feasible, should closure types be added at all? The CICE proposal
makes do without them. For ease of use with existing code, closure
types will auto-convert themselves to "single-abstract-method
interfaces" already, so with that feature, perhaps closure types
aren't needed. Then again that gets annoying with very functionally
oriented libraries. What to do, what to do ?


4) There's also continued debate about time vs. completeness. Certain
proposals are way more involved and are basically shot down due to
lack of time, but those same proposals do seem to lead to better
syntax and a more consistent language, though whether or not this is
really true once such a proposal has been fully fleshed out is
unclear, partly because there's not enough time to research it. Should
java just get closures now, period, even if it won't be as good as it
might have been, or should java either delay the release of java7 or
move closures up to java8 to provide the time to get to the best
possible proposal?

--
You received this message because you are subscribed to the Google Groups "The Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.

Viktor Klang

unread,
May 25, 2010, 10:27:18 AM5/25/10
to java...@googlegroups.com
I also want to add the debate regarding non-local returns.
--
Viktor Klang
| "A complex system that works is invariably
| found to have evolved from a simple system
| that worked." - John Gall

Akka - the Actor Kernel: Akkasource.org
Twttr: twitter.com/viktorklang

Alexey Zinger

unread,
May 25, 2010, 10:49:09 AM5/25/10
to java...@googlegroups.com
Control flow in general, up to yields.


From: Viktor Klang <viktor...@gmail.com>
To: java...@googlegroups.com
Sent: Tue, May 25, 2010 10:27:18 AM
Subject: Re: [The Java Posse] "The closure debate is pants" - No, no it isn't.

Michael Neale

unread,
May 26, 2010, 12:23:32 AM5/26/10
to The Java Posse
Another point brought up I think on the IllegalArgument podcast was
how these would interact with non java languages - ie if JDK apis
start using these closures - how will they map to other languages
model of a closure.

Rakesh

unread,
May 26, 2010, 5:42:34 AM5/26/10
to java...@googlegroups.com
I recently read Coders At Work and in the interview with Joshua Bloch,
he pretty much inferred that generics may not have been a good thing
because of the complexity it produced.

If generics had been used to restrict types in collections, fine but
people were using the <? extends Blah> and <? super Blah> too much
making things more complicated. Add to this the reification issue
mentioned previously.

Personally I think a language change should only be introduced if it
reduces the complexity (sometimes boiler plate isnt the end of the
world as long as you know what it does). I suspect closures will end
up being the next generics debacle.

R

Kevin Wright

unread,
May 26, 2010, 6:49:54 AM5/26/10
to java...@googlegroups.com
Most definitely!
Use-site variance declarations are a total pain, and worse still, they push that pain onto library consumers... never good.

For comparison, take a look at the declaration-site variance that Scala uses.  In this regards it's a very different type system to Java, made possible in part by erasure (see, it *is* good for something).  Especially when you get into the heady realms of higher-kinded types.

The use of "Manifests" and "Context Bounds" (syntactic sugar for implicit manifests) also provides almost anything that reification can provide.


The real problem now is documentation, especially the auto-generated scaladoc.  Because most of the type system complexity is pushed back onto library designers (where it rightly belongs!), some of the method signatures (in e.g. collection classes) can look just a tiny bit scary.  This obscures how easy it actually is to use these methods.

Consider something like the ++ method in List which just adds another collection to return a new list (we make everything immutable where possible in scala-land), the *true* signature (as seen by the library writer) is:

  def ++[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That

Ouch!

That's nasty for such a simple concept, and much of the difficulty is because it's actually defined in the TraversableLike trait, which allows for wide reuse across many collection classes (always a good thing).  So it may look verbose and complex at first, but it really does make life easier for library consumers allowing a style of programming very similar to that of dynamically typed languages.

Fortunately scaladoc is able to collapse down the implicit stuff and show how this signature appears when inherited by List, as should be seen by a user of the class:

  def ++(that: TraversableOnce[A]): List[A]

Easy!

Work and research is ongoing for better ways of handling this sort of thing.



--
Kevin Wright

mail/google talk: kev.lee...@googlemail.com
wave: kev.lee...@googlewave.com
skype: kev.lee.wright
twitter: @thecoda

Reinier Zwitserloot

unread,
May 26, 2010, 10:36:20 AM5/26/10
to The Java Posse
non-local returns and control flow in general are 'out' - they have
been explicitly excluded from the scope of project lambda.

However, the ability of whatever proposal makes it to be expanded
later to support of course isn't, and this is in fact a (minor) aspect
of the closure debates - how much do we paint ourselves into a corner
if we adapt closure proposal X?

On May 25, 4:27 pm, Viktor Klang <viktor.kl...@gmail.com> wrote:
> I also want to add the debate regarding non-local returns.
>

> > javaposse+...@googlegroups.com<javaposse%2Bunsubscribe@googlegroups .com>

Reinier Zwitserloot

unread,
May 26, 2010, 10:36:52 AM5/26/10
to The Java Posse
Rakesh, as I already said, closures itself are in. Folks like you that
think generics sucked and closures are too complicated lost.
Fortunately.

Viktor Klang

unread,
May 26, 2010, 10:41:34 AM5/26/10
to java...@googlegroups.com
On Wed, May 26, 2010 at 4:36 PM, Reinier Zwitserloot <rein...@gmail.com> wrote:
Rakesh, as I already said, closures itself are in. Folks like you that
think generics sucked and closures are too complicated lost.
Fortunately.

Hehe, :-)
 
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.

Rakesh

unread,
May 26, 2010, 11:27:50 AM5/26/10
to java...@googlegroups.com
oh well. Back in the box I go....

Kevin Wright

unread,
May 26, 2010, 11:29:52 AM5/26/10
to java...@googlegroups.com
There's *some* merit in objecting based on complexity

Why must it always be the case (in Java at least) that new functionality so often seems to come with a whole bucketload of new boilerplate as well?  Other languages have already shown us that this needn't be the case, and that features can be combined to offer even more elegance then either one by itself.

The enhanced-for loop was a step in the right direction, allowing me to write smaller and more elegant code.
Generics, on the other hand...



Viktor Klang

unread,
May 26, 2010, 11:35:30 AM5/26/10
to java...@googlegroups.com
On Wed, May 26, 2010 at 5:29 PM, Kevin Wright <kev.lee...@googlemail.com> wrote:
There's *some* merit in objecting based on complexity

I don't see the difference between anon classes and closures from a complexity-standpoint if we omit non-local returns and control flow.
 

Why must it always be the case (in Java at least) that new functionality so often seems to come with a whole bucketload of new boilerplate as well?  Other languages have already shown us that this needn't be the case, and that features can be combined to offer even more elegance then either one by itself.

Right now all we can do is to put lipstick on the pig :/
 

The enhanced-for loop was a step in the right direction, allowing me to write smaller and more elegant code.
Generics, on the other hand...

yeah :/
 

Alexey Zinger

unread,
May 26, 2010, 11:41:19 AM5/26/10
to java...@googlegroups.com
Generics are complex (more to produce API than consume), but I don't think it's fair to say that all it did was add boilerplate.  As a consumer of generified API, I hardly ever see casts anymore (which should count as a reduction in boilerplate as well as an improvement in type safety -- one of Java's cornerstones), I don't have to express in comments what is now both a concrete expression in the code and is picked up by javadoc.  It has made light structural typing convenient (rolling your own Pair-like constructs).
 
Alexey
2001 Honda CBR600F4i (CCS)
2002 Suzuki Bandit 1200S
1992 Kawasaki EX500
http://azinger.blogspot.com
http://bsheet.sourceforge.net
http://wcollage.sourceforge.net



From: Kevin Wright <kev.lee...@googlemail.com>
To: java...@googlegroups.com
Sent: Wed, May 26, 2010 11:29:52 AM
Subject: Re: [The Java Posse] Re: "The closure debate is pants" - No, no it isn't.

Kevin Wright

unread,
May 26, 2010, 11:45:21 AM5/26/10
to java...@googlegroups.com
Well, the promise of closures has always been to support more elegant (functional) styles of programming; so I'm ever hopeful...

I'm under no illusion that Java can ever be made as syntactically nice as a language designed with this stuff from the beginning.

But, until we can persuade everyone to move en-masse to Scala or Clojure, I *am* going to be forced into writing Java more often than I'd like, so I'd like to the the pig made up as nicely as possible.

Kevin Wright

unread,
May 26, 2010, 11:54:29 AM5/26/10
to java...@googlegroups.com
Generics have 3 big issues (from memory)

- Having specified the type parameters on declaring a variable, they must then be repeated when instantiating it.

- Co/Contravariance  (i.e. ArrayList<X extends ParentType>). Not so bad on collections, but a nightmare for more advanced structures where USERS have to get the right balance of <X extends T> vs <X super T>.  Error messages here if you get it wrong are often less than helpful. 

- Interop with non-genericised legacy code - 'nuff said!


Full kudos to Google Collections though.  They do manage to take away much of the pain from the first two points, though some people would consider the API to be non-idiomatic Java.

Reinier Zwitserloot

unread,
May 26, 2010, 7:27:42 PM5/26/10
to The Java Posse
Generics attempt to represent co- and contravariance in the type
system.

co- and contravariance are inherently complex. You can't make them non-
complex. See scala, haskell, and any other language with them. You
could go without it altogether, but then we have a dynamic typing
system - a system where by you do not know at write time what the
types of things are, and you are instead forced to re-assert the type,
over and over again. In python, a type assertion is trivial, by
writing something like:

foo[0].bar()

you are asserting that the first entry in the 'foo' list is currently
referencing an object which has a 'bar' entry, and that this 'bar'
entry is an executable function.

java does not work that way. Scala does not work that way. Haskell
does not work that way. Java DID work that way, at least in situation
where generics are now used. This is what you used to have to do:

((Foo)foo.get(0)).bar()

unwieldy and ugly, but that's a syntax issue. More to the point is
that, in a strongly and statically typed language, you are
nevertheless forced into making runtime type assertions. This does not
smack of a consistent language (the very consistency Kevin, Rakesh,
and Viktor seem to want!). Generics gets us back to:

foo.get(0).bar()

which is as it ought to be. If you want this to work correctly, then
you need generics. Show me a language that's statically typed which
nevertheless avoids generics. It'll be about as complex as generics is
(though reification _CAN_ help; lack of reification is a separate
issue, to wit: Java has raised backwards compatibility above all other
concerns. Hem and haw about other languages doing it better all you
want, but when you are the world's most popular programming language,
especially in large teams, you can't break old code. It would have
been great if java had more support to evolve the language, but it
doesn't, and more to the point, few other languages do. That's a hard
nut to crack).

On May 26, 5:29 pm, Kevin Wright <kev.lee.wri...@googlemail.com>
wrote:


> There's *some* merit in objecting based on complexity
>
> Why must it always be the case (in Java at least) that new functionality so
> often seems to come with a whole bucketload of new boilerplate as well?
>  Other languages have already shown us that this needn't be the case, and
> that features can be combined to offer even more elegance then either one by
> itself.
>
> The enhanced-for loop was a step in the right direction, allowing me to
> write smaller and more elegant code.
> Generics, on the other hand...
>

> On 26 May 2010 15:41, Viktor Klang <viktor.kl...@gmail.com> wrote:

> >> javaposse+...@googlegroups.com<javaposse%2Bunsubscribe@googlegroups .com>


> >> .
> >> > >> For more options, visit this group athttp://
> >> groups.google.com/group/javaposse?hl=en.
>
> >> > > --
> >> > > You received this message because you are subscribed to the Google
> >> Groups "The Java Posse" group.
> >> > > To post to this group, send email to java...@googlegroups.com.
> >> > > To unsubscribe from this group, send email to

> >> javaposse+...@googlegroups.com<javaposse%2Bunsubscribe@googlegroups .com>


> >> .
> >> > > For more options, visit this group athttp://
> >> groups.google.com/group/javaposse?hl=en.
>
> >> --
> >> You received this message because you are subscribed to the Google Groups
> >> "The Java Posse" group.
> >> To post to this group, send email to java...@googlegroups.com.
> >> To unsubscribe from this group, send email to

> >> javaposse+...@googlegroups.com<javaposse%2Bunsubscribe@googlegroups .com>


> >> .
> >> For more options, visit this group at
> >>http://groups.google.com/group/javaposse?hl=en.
>
> > --
> > Viktor Klang
> > | "A complex system that works is invariably
> > | found to have evolved from a simple system
> > | that worked." - John Gall
>
> > Akka - the Actor Kernel: Akkasource.org
> > Twttr: twitter.com/viktorklang
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "The Java Posse" group.
> > To post to this group, send email to java...@googlegroups.com.
> > To unsubscribe from this group, send email to

> > javaposse+...@googlegroups.com<javaposse%2Bunsubscribe@googlegroups .com>


> > .
> > For more options, visit this group at
> >http://groups.google.com/group/javaposse?hl=en.
>
> --
> Kevin Wright
>

> mail/google talk: kev.lee.wri...@googlemail.com
> wave: kev.lee.wri...@googlewave.com
> skype: kev.lee.wright
> twitter: @thecoda

Reinier Zwitserloot

unread,
May 26, 2010, 7:35:12 PM5/26/10
to The Java Posse
Repetition of generics in type declaration and object instantiation go
away in java 7. This is already in the java 7 nightlies:

List<String> list = new ArrayList<>();

(Backwards compatibility again rears its head here: new ArrayList()
wasn't possible because in current java that means: "raw type", and
backwards compatibility means semantic means cannot change. If I were
the dictator I'd drop it here, but I'm not, and while I don't agree I
can see why its done this way).

Co/Contravariance is how the world works. You can't just wave your
hand and make it go away. It is entirely possible to say: I take a
list and I add integers to it. You can supply such a method with a
List containing anything from Integer on down to Object and nothing
will break, hence you need a way to say: List<? super Integer>. If you
couldn't say that, you lose the ability to create a method that says:
I take any list that will let me add integers to it. What do you
propose, exactly? Drop 'extends' and 'super' altogether? There are
massive amounts of things you can then no longer do. For example, you
then can't run someNumberList.addAll(someIntegerList). Alternatively
you make generics a glorified comment and don't do any compile-time
checking at all, instead relying entirely on runtime exceptions.
That's one way to design a language. I suggest you use jython instead
of living in the fantasy that java is somehow dynamic, or should be.

It's true the error messages could use some more support, but compared
to other languages, javac's messages are stellar, so evidently that's
a hard thing to get right. I am of half a mind to dive into javac
itself and send some patches; javac is now open source, so, anyone can
contribute!

Interop: Yup. It's a pain, isn't it?

On May 26, 5:54 pm, Kevin Wright <kev.lee.wri...@googlemail.com>
wrote:


> Generics have 3 big issues (from memory)
>
> - Having specified the type parameters on declaring a variable, they must
> then be repeated when instantiating it.
>
> - Co/Contravariance  (i.e. ArrayList<X extends ParentType>). Not so bad on
> collections, but a nightmare for more advanced structures where USERS have
> to get the right balance of <X extends T> vs <X super T>.  Error messages
> here if you get it wrong are often less than helpful.
>
> - Interop with non-genericised legacy code - 'nuff said!
>
> Full kudos to Google Collections though.  They do manage to take away much
> of the pain from the first two points, though some people would consider the
> API to be non-idiomatic Java.
>

> On 26 May 2010 16:41, Alexey Zinger <inline_f...@yahoo.com> wrote:
>
>
>
> > Generics are complex (more to produce API than consume), but I don't think
> > it's fair to say that all it did was add boilerplate.  As a consumer of
> > generified API, I hardly ever see casts anymore (which should count as a
> > reduction in boilerplate as well as an improvement in type safety -- one of
> > Java's cornerstones), I don't have to express in comments what is now both a
> > concrete expression in the code and is picked up by javadoc.  It has made
> > light structural typing convenient (rolling your own Pair-like constructs).
>
> > Alexey
> > 2001 Honda CBR600F4i (CCS)
> > 2002 Suzuki Bandit 1200S
> > 1992 Kawasaki EX500
> >http://azinger.blogspot.com
> >http://bsheet.sourceforge.net
> >http://wcollage.sourceforge.net
>

> > ------------------------------
> > *From:* Kevin Wright <kev.lee.wri...@googlemail.com>
> > *To:* java...@googlegroups.com
> > *Sent:* Wed, May 26, 2010 11:29:52 AM
> > *Subject:* Re: [The Java Posse] Re: "The closure debate is pants" - No, no


> > it isn't.
>
> > There's *some* merit in objecting based on complexity
>
> > Why must it always be the case (in Java at least) that new functionality so
> > often seems to come with a whole bucketload of new boilerplate as well?
> >  Other languages have already shown us that this needn't be the case, and
> > that features can be combined to offer even more elegance then either one by
> > itself.
>
> > The enhanced-for loop was a step in the right direction, allowing me to
> > write smaller and more elegant code.
> > Generics, on the other hand...
>

> > On 26 May 2010 15:41, Viktor Klang <viktor.kl...@gmail.com> wrote:

> >>> javaposse+...@googlegroups.com<javaposse%2Bunsubscribe@googlegroups .com>


> >>> .
> >>> > >> For more options, visit this group athttp://
> >>> groups.google.com/group/javaposse?hl=en.
>
> >>> > > --
> >>> > > You received this message because you are subscribed to the Google
> >>> Groups "The Java Posse" group.
> >>> > > To post to this group, send email to java...@googlegroups.com.
> >>> > > To unsubscribe from this group, send email to

> >>> javaposse+...@googlegroups.com<javaposse%2Bunsubscribe@googlegroups .com>


> >>> .
> >>> > > For more options, visit this group athttp://
>

> ...
>
> read more »

Viktor Klang

unread,
May 27, 2010, 1:32:00 AM5/27/10
to java...@googlegroups.com
On Thu, May 27, 2010 at 1:35 AM, Reinier Zwitserloot <rein...@gmail.com> wrote:
Repetition of generics in type declaration and object instantiation go
away in java 7. This is already in the java 7 nightlies:

List<String> list = new ArrayList<>();

(Backwards compatibility again rears its head here: new ArrayList()
wasn't possible because in current java that means: "raw type", and
backwards compatibility means semantic means cannot change. If I were
the dictator I'd drop it here, but I'm not, and while I don't agree I
can see why its done this way).

Co/Contravariance is how the world works. You can't just wave your
hand and make it go away. It is entirely possible to say: I take a
list and I add integers to it. You can supply such a method with a
List containing anything from Integer on down to Object and nothing
will break, hence you need a way to say: List<? super Integer>. If you
couldn't say that, you lose the ability to create a method that says:
I take any list that will let me add integers to it. What do you
propose, exactly? Drop 'extends' and 'super' altogether? There are
massive amounts of things you can then no longer do. For example, you
then can't run someNumberList.addAll(someIntegerList). Alternatively
you make generics a glorified comment and don't do any compile-time
checking at all, instead relying entirely on runtime exceptions.
That's one way to design a language. I suggest you use jython instead
of living in the fantasy that java is somehow dynamic, or should be.

Reinier, the debate was definition-site variance annotations or callsite variance annotations,
not about dropping variance. (Not from my perspective anyways)

 
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.

Reinier Zwitserloot

unread,
May 27, 2010, 8:03:39 AM5/27/10
to The Java Posse
I don't think the closure debates include anything on verbosity of
generics, that's project coin's bailiwick.

I don't think I understand what you're asking / pointing out.

On May 27, 7:32 am, Viktor Klang <viktor.kl...@gmail.com> wrote:

> ...
>
> read more »

Viktor Klang

unread,
May 27, 2010, 8:15:14 AM5/27/10
to java...@googlegroups.com
On Thu, May 27, 2010 at 2:03 PM, Reinier Zwitserloot <rein...@gmail.com> wrote:
I don't think the closure debates include anything on verbosity of
generics, that's project coin's bailiwick.

There were two parallel discussions, one about closures, and one about generics.
i'm referrign to the one about generics, which contained a discussion on co and contra variance.
Does any of this ring any bells?
 
> ...
>
> read more »

--
You received this message because you are subscribed to the Google Groups "The Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.

Reinier Zwitserloot

unread,
May 27, 2010, 2:16:31 PM5/27/10
to The Java Posse
I haven't seen any proposals back then or now or know of any languages
that use "call site variance". Can you elaborate a bit on what this
means?


On May 27, 2:15 pm, Viktor Klang <viktor.kl...@gmail.com> wrote:

> ...
>
> read more »

Kevin Wright

unread,
May 27, 2010, 2:39:31 PM5/27/10
to java...@googlegroups.com

The obvious answer here is: Java uses call-site variance

On 27 May 2010 19:16, "Reinier Zwitserloot" <rein...@gmail.com> wrote:

I haven't seen any proposals back then or now or know of any languages
that use "call site variance". Can you elaborate a bit on what this
means?


On May 27, 2:15 pm, Viktor Klang <viktor.kl...@gmail.com> wrote:
> On Thu, May 27, 2010 at 2:03 PM, Reinier Zwitserloot <reini...@gmail.com>wrote:

>
> > I don't think the closure debates include anything on verbosity of

> > generics, that's projec...

> ...
>
> read more »

--
You received this message because you are subscribed to the Google Groups...

phnature

unread,
May 27, 2010, 4:54:23 PM5/27/10
to The Java Posse
this is just in! Initial Project Lambda prototype code push: http://bit.ly/a2SApZ

On May 27, 2:39 pm, Kevin Wright <kev.lee.wri...@googlemail.com>
wrote:


> The obvious answer here is: Java uses call-site variance
>

Reinier Zwitserloot

unread,
May 27, 2010, 9:04:12 PM5/27/10
to The Java Posse
What is this, a terrorist interrogation? Stop being flippant. What do
you mean with declaration site variance then?

On May 27, 8:39 pm, Kevin Wright <kev.lee.wri...@googlemail.com>
wrote:
> The obvious answer here is: Java uses call-site variance
>

Ben Schulz

unread,
May 27, 2010, 11:07:29 PM5/27/10
to The Java Posse
On 28 Mai, 03:04, Reinier Zwitserloot <reini...@gmail.com> wrote:
> What is this, a terrorist interrogation? Stop being flippant.
Wow! Just wow.

> What do you mean with declaration site variance then?
When declaring a type with a type parameter T that only appears in co/
contravariant position you mark it as such. Iterator<Integer> becomes
a subtype of Iterator<Number>, Comparable<Number> a subtype of
Comparable<Integer>.

interface Iterator<+T> { /* ... */ }

With kind regards
Ben

Michael Neale

unread,
May 28, 2010, 12:30:59 AM5/28/10
to The Java Posse
There isn't enough use of the term "bailiwick" in my books. Thats the
second time I have heard of it in 6 months (last was in terms of
DNS).
> ...
>
> read more »

Viktor Klang

unread,
May 28, 2010, 6:14:51 AM5/28/10
to java...@googlegroups.com
So the question is, are everyone on the same page now regarding call-site and declaration site variance annotations?

> ...
>
> read more »

--
You received this message because you are subscribed to the Google Groups "The Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.

Kevin Wright

unread,
May 28, 2010, 5:42:07 AM5/28/10
to java...@googlegroups.com
Sorry I couldn't give a full reply sooner!  The android phone may be a wonderful thing, but it's ill-suited for writing small essays :)


To elaborate then... First consider the Lists.transform method in Google Collections, with the signature:

    public static <F,T> List<T> transform(
        List<F> fromList,
        Function<? super F,? extends T> function)

    note: the type names represent (F)rom and (T)o


This will take a List[F] and apply a `Function` (poor-man's closure :P) to every element in that list, resulting in a List[T]

So what, exactly, does `Function<? super F,? extends T>` mean?
A Function<F, T> will take an object of type F and yield an object of type T
But, in this case, it's acceptable to use a Function that will take a supertype of F and yield a subtype of T. 

Think about it...  If we're converting a List of ProjectManagers to a List of PhoneNumbers, then it's perfectly valid to use a function that converts an Employee to a MobileNumber (if a ProjectManager IS-AN Employee and a MobileNumber IS-A PhoneNumber)

This variance is actually an inherent property of Functions, and everywhere a function is used then the correct signature is 'Function<? super F,? extends T>' - unless you have some crazy inheritance tree that totally violates the Liskov Substitution Principle (in which case you have other problems...).

In formal language, Function is said to be contra-variant in F and co-variant in T.

With the Java scheme, this variance should really be specified in *every single place* that Function is used as an argument, hence the moniker 'call-site' variance.  It also means that boilerplate can explode very quickly if you want to write your own code that consumes such Function objects.



And now to Scala's approach

Under Scala, the variance information is pushed back into the declaration of the Function1 Trait
For now, just think of `trait` as another word for `interface` (in this case, that's *exactly* what it is)
We're also using Function1, as Scala supports Function2, Function3, etc. for different numbers of arguments.
Other syntactic differences should be largely intuitive :)

    trait Function1[-T1, +R] extends AnyRef

    (where Tx is the type of argument x, and R is the type of the Result)

The important bit is the +/- annotation on the type parameters.  Think of `-` as `extends` and `+` as `super`
What this essentially tells the compiler is that:

    Function1[Employee, MobileNumber]  IS-A  Function1[ProjectManager, PhoneNumber]

    (assuming MobileNumber IS-A PhoneNumber and ProjectManager IS-AN Employee)

You can now specify the original Transform method as:

    def transform[F,T](fromList : List[F], func: Function1[F,T]) : List[T]= ...

A big boilerplate saving for consumers of Functions, lots of repetition is cut out, and so there are fewer place to make mistakes.  It now becomes the responsibility of library designers (who really ought to know!) to ensure that variance is correctly specified, and users can happily consume these things trusting that they will just behave in a logical fashion.




Reinier Zwitserloot

unread,
May 28, 2010, 11:39:40 AM5/28/10
to The Java Posse
Ah, that helps.

There remain things you cannot do with that which are perfectly
possible with java's generics. For example:

someNumberList.addAll(someIntegerList);

For that to work, a List<Integer> has to be some sort of List<Number>,
so List would need to be declared as List[+T]. However, if you do
that, you break things:

List<Number> n = new ArrayList<Number>();
List<Integer> i = n; //legal if List is declared as List[+T].
n.add(someDouble);
i.get(0); //invisible ClassCastException because we caused heap
pollution!

It would have been fine if List was immutable. But it isn't. Further
complicating matters, if we lived in a perfect world and the list
hierarchy was split into:

interface List //Contains only the 'read' methods and implies neither
immutability nor mutability
interface ImmutableList extends List //Does not add any methods, but
implies immutability
interface MutableList extends List //Adds set, clear, add, addAll,
retain, etc methods, implies mutability

Then if List was declared as List[+T] you'd still run into trouble
because there's a mutable subtype of it. Therefore, List would have to
remain List[T], but then ImmutableList overrides this with List[+T].
That would get very hairy very fast, and be a not-fun source of
puzzlers. Instead you'd have to get rid of List altogether and have
two completely separate trees: ImmutableList and MutableList.
Possible, but a gigantic departure from how java works today, and also
a cost.

Therefore, the only pragmatic solution seems to be to then have both
in the language - call-site AND declare-site. I haven't thought
through all the repercussions, but at-a-glance I would probably like
it. Though, it's pretty much by definition _more_ complicated than
java's current generics, as it has all the syntactic shackles of the
current generics, and in addition to that, a way to specify that
certain types are universally co- and/or contra-variant on some type
parameter. Given that this thread has already highlighted complaints
about generics being 'too complicated', I don't think having the
ability to declare universal co/contravariance on type parameters
would assuage the complaints. Unless, perhaps, the complaints are of
the never-use-focus-groups kind: Folks are unhappy about generics for
some reason they don't themselves understand, so they jump on a wrong
but plausible explanation for _why_ they think generics is bad, and
then complain using this wrong but plausible explanation. i.e.:
"generics is too complicated" when actually what is meant is:
"Generics isn't complicated enough; what I really want, is declaration-
site variance!"


Actually you could also solve the dilemma by putting your foot down
and stating that java "just can't do" mixed co/contravariance on a
single type parameter, disabling the ability to add all elements in a
list-of-integers to a list-of-numbers. There's some precedence; Java
can't do higher order generics either - a language doesn't have to
choose to allow every single last possible kind of code. However,
mixed co/contravariance tends to show up with mutable objects, and
java's existing libraries (not just rt.jar, but the entire greater
java community's library code) is riddled with mutable objects, so not
being able to express mixed co/contravariance would be a big problem.



On May 28, 11:42 am, Kevin Wright <kev.lee.wri...@googlemail.com>
wrote:
> On 27 May 2010 19:39, Kevin Wright <kev.lee.wri...@googlemail.com> wrote:
>
>
>
>
>
> > The obvious answer here is: Java uses call-site variance
>
> > On 27 May 2010 19:16, "Reinier Zwitserloot" <reini...@gmail.com> wrote:
>
> > I haven't seen any proposals back then or now or know of any languages
> > that use "call site variance". Can you elaborate a bit on what this
> > means?
>
> > On May 27, 2:15 pm, Viktor Klang <viktor.kl...@gmail.com> wrote:
> > > On Thu, May 27, 2010 at 2:03 PM, Reinier Zwitserloot <reini...@gmail.com
> > >wrote:
>
> > > > I don't think the closure debates include anything on verbosity of
> > > > generics, that's projec...
>
> > > ...
>
> > > read more »
>
> > --
> > You received this message because you are subscribed to the Google
> > Groups...
>
> --
> Kevin Wright
>

Kevin Wright

unread,
May 28, 2010, 12:43:54 PM5/28/10
to java...@googlegroups.com
Responses in-line:


On 28 May 2010 16:39, Reinier Zwitserloot <rein...@gmail.com> wrote:
Ah, that helps.

There remain things you cannot do with that which are perfectly
possible with java's generics. For example:

someNumberList.addAll(someIntegerList);


Perfectly valid, so if we assume the existence of some type `Number`, I would write (in Scala)

val numlist = List(2.3f, 6.9d, 7l) //List[Number] - as the closest common supertype of args is inferred
val intlist = List(1, 2, 3) //this is a List[Int]
numlist ++= intlist 

(the ++= operator adds a collection to an existing collection, mutating it to do so. By comparison, += would add a single element and ++ returns a new collection containing the union without mutating the original)

numlist is still a List[Number] and continues to be valid, as anything you pull out of it is a number

 

For that to work, a List<Integer> has to be some sort of List<Number>,
so List would need to be declared as List[+A]. However, if you do

that, you break things:

List<Number> n = new ArrayList<Number>();
List<Integer> i = n; //legal if List is declared as List[+T].
n.add(someDouble);
i.get(0); //invisible ClassCastException because we caused heap
pollution!

This is invalid.  As Int is a subclass of Number, then List[Int] is a subclass of List[Num] (i.e. it's covariant)
You've described the contravariant opposite (i.e. List[-T]) which is possible to define, but why would anyone do so?

As an interesting aside though, you can do EXACTLY this sort of thing with standard Java arrays, it isn't pretty.

 
It would have been fine if List was immutable. But it isn't. Further
complicating matters, if we lived in a perfect world and the list
hierarchy was split into:

interface List //Contains only the 'read' methods and implies neither
immutability nor mutability
interface ImmutableList extends List //Does not add any methods, but
implies immutability
interface MutableList extends List //Adds set, clear, add, addAll,
retain, etc methods, implies mutability

Then if List was declared as List[+T] you'd still run into trouble
because there's a mutable subtype of it. Therefore, List would have to
remain List[T], but then ImmutableList overrides this with List[+T].
That would get very hairy very fast, and be a not-fun source of
puzzlers. Instead you'd have to get rid of List altogether and have
two completely separate trees: ImmutableList and MutableList.
Possible, but a gigantic departure from how java works today, and also
a cost.


Yes, Mutable and Immutable collections should be kept completely separate, as you correctly identify.

I would consider creating a mutable subclass of an immutable collection to be a gross violation of the Liskov Substitution Principle and would not be at all happy to see such a thing!

But you miss the point of Immutable collections, they can and do have methods for adding/removing/changes elements.  The trick is that this is achieved by returning a new collection with the modifications in place.

So lets state that our List[+A] is immutable, and we want to add a list of integers to a list of doubles.
(I'll also assume that int is a subclass of double.  Not strictly true, but close enough)

    val dbllist = List(2.7d, 9.6d, 3.1d)
    val intlist = List(1, 2, 3)
    val numlist = intlist ++ dbllist 

to do this, we use more type parameters on the definition of ++, and also introduce Type bounds:

    def ++[B >: A](other : List[B]): List[B]

This introduces a new parameter, B, and specifies that it can be a supertype of A (the List's type param) via the type bound notation `>:`.  This will concatenate the two lists and return a new list of type B

So numlist is a list of doubles.  The original intlist remains type-safe and unchanged. The world is good

What if we swap the lists?

    val numlist = dbllist ++ intlist 

Then the compiler will automatically treat the intlist as a list of doubles (remember, it's a subtype) and add it, once again yielding a new list of doubles.


Similar logic is also used on methods that add single members, so
  
  5.6d :: intlist

will also return a list of doubles

(:: is the cons operator, it just prepends an element to an existing list, it's very common in functional programming)


 
--

You received this message because you are subscribed to the Google Groups "The Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.




--
Kevin Wright

mail/google talk: kev.lee...@gmail.com
Reply all
Reply to author
Forward
0 new messages