thoughts on Java closures, Groovy, et al

瀏覽次數:33 次
跳到第一則未讀訊息

RogerV

未讀,
2007年3月17日 下午5:38:512007/3/17
收件者:The Java Posse
Closures in Groovy work naturally and are entirely effective. They
soon predominate the way one codes in Groovy and really is the
hallmark characteristic feature of this language.

Closure proposals for the Java language all stink.

Other proposed Java language features, such as xml literals, also
stink. Groovy XML markup is a much superior concept.

AspectJ is far too much down a AOP rabbit hole for most people to
stomach. (I rather like what can be done with AspectJ and love using
its power for patching 3rd party libraries - but would be first to
admit it'll never catch on as mainstream.) Alternatively, adding new
methods in an AOP-like (or C# extensions methods-like manner) to
existing classes using Groovy is painless, as is the gamut of meta
programming features in Groovy.

LINQ has turned C# into a bloated PL/1 language monstrosity - now
looks like a public works project for language designers. (One can
tell C# has now fallen into the disrepute of bloat-ware by looking at
the C# 2.0 addition of anonymous delegates. This feature is entirely
dissatisfying to use. They're throwing in features without much regard
for what it will be like in practice to use those features.) In
contrast the Groovy feature to convert closures of Groovy language
into equivalent SQL expression is light weight and satisfying. A good
balance has been struck.

I've merely scratched the surface of examples, but my point is that
Groovy is already the next evolutionary iteration of the Java
language. There is no need to add any new features to Java as doing so
will fall into the same quagmire that C# 2.0/3.0 have fallen into.
Groovy already has some feature or characteristic that addresses
nearly every point of critique that has been leveled at Java the
language over the last five or so years.

reinierz

未讀,
2007年3月19日 上午9:19:362007/3/19
收件者:The Java Posse
Wow. Do you earn money everytime a soul converts to Groovy? I mean,
talk about a pitch....

Not that you're basically right. Groovy's take on closures is pretty
neat.

However, groovy isn't java. What it is, is very compatible with java.
Which means java needs the very niche I've always advocated: Typing,
Typing, Typing, and more Typing, to a fault.

So bring on the NonNull types. Make n-tuple generics possible. Give us
CICE (because it types far more than BGGA (Gafter's) closures). If
typing is more useful for a given project, then you're already going
to go with java, whereas if typing isn't your cup of tea, picking java
is a mistake in the first place.

However, what you're proposing is tantamount to declaring Groovy to be
equal to java 1.8, and essentially @Deprecating java. That's a bit
much, not?

Re: Your analysis that C# is well on its way to becoming perl soup:
Good eye. Absolutely true.

RogerV

未讀,
2007年3月19日 下午3:04:192007/3/19
收件者:The Java Posse
C# is a strongly typed language ala Java.

In 2.0 and 3.0 definitions of that language, many new features have
been added. Some of them cover turf that is being contemplated for
Java.

Am concerned that trying to accommodate some of these popular language
features might not be possible to do with sufficient grace in a
strongly typed language.

Groovy is not held back by that predicament as is Java. It permits
typed variables, but they are optional. Groovy code can thus be
written with conciseness and grace.

>From experience of using C#, have begun to ponder if Java 5 is far
enough to take a strongly typed language.

Yet maybe am completely wrong about this. Maybe a new strongly typed
language could be created that has these cool concepts, yet is a
graceful language to write in. It might be that C# and Java are both
just encumbered by their respective baggage.

Casper Bang

未讀,
2007年3月19日 下午4:37:362007/3/19
收件者:The Java Posse
> Yet maybe am completely wrong about this. Maybe a new strongly typed
> language could be created that has these cool concepts, yet is a
> graceful language to write in. It might be that C# and Java are both
> just encumbered by their respective baggage.

What's the clumsy part of C#'s type inferencing system or lambda
functions? It is still very much a statically typed language if you
ask me. Post-generics collection classes, int pseudo-enums and code
labels are examples of non-graceful things in my book.

/Casper

Alexey Zinger

未讀,
2007年3月19日 下午4:58:142007/3/19
收件者:java...@googlegroups.com
I find this debate ironic.  I've been a long time user of BeanShell -- another loosely typed Java-based language that allows prototyping and had autoboxing before Java did.  In general though, it's hailed as a Java superset.  It's pretty neat to see what people do with it.  Anyway, one of the things that have come up on the BeanShell discussion board lately is the feature request to be able to preload scripts and verify their integrity.  Obviously, since it's a dynamic language, it's not the easiest thing in the world to do.  And here, we've have Java-heads asking for dynamic language features.  I think we've got it already.  We have dynamic languages that can interface with JVM and load Java libraries.  Therefore, we can distribute code to and from the dynamic environment as we see fit.

RogerV <rog...@qwest.net> wrote:

TV dinner still cooling?
Check out "Tonight's Picks" on Yahoo! TV.

RogerV

未讀,
2007年3月19日 晚上10:48:442007/3/19
收件者:The Java Posse
Casper, was going to concede that C# type inference var declaration
does indeed pan out as graceful. Used with tuples (anonymous classes),
this feature all seems to work out - while preserving strong typing!
To my knowledge, though, this is one cool feature item of C# that I
don't think anyone in Java community has paid attention to.

Not sure yet on lambda functions. My reaction in watching C# compiler
guys showing off code examples utilizing this, was that things started
to turn esoteric. BTW, is the bulk of the Java programming community
ready to go down the functional programming rabbit hole? Is the
concept of currying, et al, ready for the mainstream?

Now I was at first excited about anonymous delegates, but the
experience of trying to use that feature has rather soured me on them.
This is just a stark example of adding something to a language that
was a good idea on paper but stinks in practice. It is exactly this
that am fearful for Java in regard to closures.

Alexey Zinger

未讀,
2007年3月19日 晚上11:21:252007/3/19
收件者:java...@googlegroups.com
I don't get it.  Don't we have closure functionality in Java now via anonymous classes?  From Wikipedia page on closures <http://en.wikipedia.org/wiki/Closure_%28computer_science%29>:

     public final void calculateInSeparateThread(final URI uri) {
// The expression "new Runnable() { ... }" is an anonymous class.
Runnable runner = new Runnable() {
void run() {
// It can access final local variables:
calculate(uri);
// It can access private fields of the enclosing class:
// Always update the Graphic components into the Swing Thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
btnSave.setEnabled(true);
}
});
}
};
new Thread(runner).start();
}


RogerV <rog...@qwest.net> wrote:

Casper, was going to concede that C# type inference var declaration
does indeed pan out as graceful. Used with tuples (anonymous classes),
this feature all seems to work out - while preserving strong typing!
To my knowledge, though, this is one cool feature item of C# that I
don't think anyone in Java community has paid attention to.

Not sure yet on lambda functions. My reaction in watching C# compiler
guys showing off code examples utilizing this, was that things started
to turn esoteric. BTW, is the bulk of the Java programming community
ready to go down the functional programming rabbit hole? Is the
concept of currying, et al, ready for the mainstream?

Now I was at first excited about anonymous delegates, but the
experience of trying to use that feature has rather soured me on them.
This is just a stark example of adding something to a language that
was a good idea on paper but stinks in practice. It is exactly this
that am fearful for Java in regard to closures.

On Mar 19, 1:37 pm, "Casper Bang" wrote:
> > Yet maybe am completely wrong about this. Maybe a new strongly typed
> > language could be created that has these cool concepts, yet is a
> > graceful language to write in. It might be that C# and Java are both
> > just encumbered by their respective baggage.
>
> What's the clumsy part of C#'s type inferencing system or lambda
> functions? It is still very much a statically typed language if you
> ask me. Post-generics collection classes, int pseudo-enums and code
> labels are examples of non-graceful things in my book.
>
> /Casper



Be a PS3 game guru.
Get your game face on with the latest PS3 news and previews at Yahoo! Games.

RogerV

未讀,
2007年3月20日 凌晨3:17:132007/3/20
收件者:The Java Posse
Just between you and me, Alexey, I'm rather with you on that.
Anonymous inner classes do rather meet the "walks like a duck, quacks
like a duck" manner of test. And as far as so-called Java closures go,
I'd vote to leave it right there. Things start getting weird in Java
when going beyond this point.

If you think about it, the current approach of implementing method(s)
on an interface is an ideal way to deal with issues such as Java's
checked exceptions (ah, is no different than for any regular method -
well, because it's just a method too). And I've never found declaring
some locals as final an onerous problem in order for them to become so-
called "member variables" of the generated anonymous inner class.

J. McConnell

未讀,
2007年3月20日 上午11:11:472007/3/20
收件者:java...@googlegroups.com
On 3/19/07, Alexey Zinger <inlin...@yahoo.com> wrote:
I don't get it.  Don't we have closure functionality in Java now via anonymous classes?  From Wikipedia page on closures < http://en.wikipedia.org/wiki/Closure_%28computer_science%29>:

     public final void calculateInSeparateThread(final URI uri) {
// The expression "new Runnable() { ... }" is an anonymous class.
Runnable runner = new Runnable() {
void run() {
// It can access final local variables:
calculate(uri);
// It can access private fields of the enclosing class:
// Always update the Graphic components into the Swing Thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
btnSave.setEnabled(true);
}

});
}
};
new Thread(runner).start();
}

But this is taken from the "Closure-like constructs in other languages" section of the article.  Anonymous inner classes, while useful, are not closures.  Neal Gafter provides a great explanation of why here:

http://gafter.blogspot.com/2007/01/definition-of-closures.html

- J.

Alexey Zinger

未讀,
2007年3月20日 下午2:03:512007/3/20
收件者:java...@googlegroups.com
Call me dense, but I still don't understand.  His main problems with anonymous classes are:

Verbose, extra wordy (isn't that what verbose means?), clumsy, redundant -- syntax sugar.

Incomplete capture of lexical context -- one man's explicit declaration of what free variables the anonymous class refers to is another man's incomplete capture of lexical context I guess.  I see nothing wrong with using EnclosingClass.this.foo. Also, much of his pains with return types and exception throwing is just a matter of properly laying out your API.  Something we should strive for regardless.

Control abstraction not possible -- yes it is.  It's just a matter of how you lay out your API.  All he's saying is he doesn't wanna have to write generic enough interfaces that encompass exception handling and return types.  Here's an example:

I've created a BeanShell-based Java spreadsheet.  BeanShell is a Java syntax superset, so the BeanShell code will be obvious to everyone here.  A spreadsheet is by its very nature a kind of functional language environment, something that relies on closures heavily.  Immediately, I realized I had a need for a closure-like construct.  I did not want to much with the syntax if I could help it.  I did have a concept of cell ranges expressed as an iterator of their addresses.  A typical use of a closure here would be to express subrange selection in an each-member-such-that way.  I therefore created a conditional iterator that took a condition object.  All I had to do then was create an evaluated expression condition implementation that took a string value signifying some expression that got evaluated:

public class InterpretedCondition implements ConditionalIterator.Condition
{
    protected final BSHTableModel model;
    protected final String expression;

    public InterpretedCondition(BSHTableModel model, String expression)
    {
        this.model = model;
        this.expression = expression;
    }

    public boolean isSatisfied(Object objectValue)
    {
        try
        {
            Interpreter bsh = this.model.bsh;
            bsh.set("val", objectValue);
            Object result = bsh.eval(this.expression);
            return result != null
                && result instanceof Boolean
                && ((Boolean)result).booleanValue();
        }
        catch(EvalError ex)
        {
            ex.printStackTrace();
        }
        return false;
    }
}


And it's used like this ( [...] syntax is specific to the spreadsheet app; condition is a command that invokes a conditional iterator using the ):

sum(condition([c10:c200], "deref(val, 1, 0) != null && (deref(val, 1, 0).indexOf([-1-0]) > -1)"))

This statement adds numbers within the range of c10:c200 that are next to a cell ( deref(val, 1, 0) ) containing the text that's next to the cell we're in ( [-1-0] ).  Much the same code can be used within compiled Java itself, minus the special cell reference syntax of course.  We can throw the same exceptions.  We can return early.  We can do all the same stuff this guy is complaining about being difficult without changing the language.  I get the feeling he wants a functional language, which Java isn't.  But he can have things closer to functional languages thanks to the multitude of different dynamic languages available today that integrate with JVM.  I believe there's even a project for porting Haskel to JVM.  So I really don't see a need for modifying the Java language itself.

"J. McConnell" <jdo...@gmail.com> wrote:
href="http://wcollage.sourceforge.net">http://wcollage.sourceforge.net


Need Mail bonding?
Go to the Yahoo! Mail Q&A for great tips from Yahoo! Answers users.

reinierz

未讀,
2007年3月21日 清晨6:40:172007/3/21
收件者:The Java Posse
You can have your cake (consider anonymous classes java's closures)
and eat it too (reduce verbiage and make that "final" stuff easier to
handle):

CICE is an alternative closure proposal that is just some light
syntactic sugaring over what's already there. I think this is the
single greatest improvement to the java language that can possibly be
made:

http://crazybob.org/2006/10/java-closure-spectrum.html

> "J. McConnell" <jdo...@gmail.com> wrote: On 3/19/07, Alexey Zinger <inline_f...@yahoo.com> wrote: I don't get it. Don't we have closure functionality in Java now via anonymous classes? From Wikipedia page on closures <http://en.wikipedia.org/wiki/Closure_%28computer_science%29>:


>
> public final void calculateInSeparateThread(final URI uri) {
> // The expression "new Runnable() { ... }" is an anonymous class.
>
> Runnable runner = new Runnable() {
> void run() {
> // It can access final local variables:
> calculate(uri);
> // It can access private fields of the enclosing class:
>
> // Always update the Graphic components into the Swing Thread
> SwingUtilities.invokeLater(new Runnable() {
> public void run() {
>
> btnSave.setEnabled(true);
> }
>
> });
> }
> };
> new Thread(runner).start();
> }
>
> But this is taken from the "Closure-like constructs in other languages" section of the article. Anonymous inner classes, while useful, are not closures. Neal Gafter provides a great explanation of why here:
>
> http://gafter.blogspot.com/2007/01/definition-of-closures.html
>
> - J.
>

> Alexey
> 2001 Honda CBR600F4i (CCS)
> 1992 Kawasaki EX500http://azinger.blogspot.comhttp://bsheet.sourceforge.nethttp://wcollage.sourceforge.net
>
> ---------------------------------

J. McConnell

未讀,
2007年3月21日 上午10:48:462007/3/21
收件者:java...@googlegroups.com
On 3/21/07, reinierz <rein...@gmail.com> wrote:

You can have your cake (consider anonymous classes java's closures)
and eat it too (reduce verbiage and make that "final" stuff easier to
handle):

CICE is an alternative closure proposal that is just some light
syntactic sugaring over what's already there. I think this is the
single greatest improvement to the java language that can possibly be
made:

I'm familiar with CICE and I would be happy to have CICE over nothing, but the fact is that anonymous inner classes are not closures for the reasons Gafter puts forward, so CICE doesn't give Java closures.  Although I would find CICE useful, I think it would be a mistake to go half-way with closures support.  If you are going to pull in much of the syntax anyway for CICE, why not bring in the other supporting functionality that BGGA requires like function types and a null type?

The arguments I've heard so far pretty much boil down to the fact that it just complicates the language too much.  Complicates it for who?  It might complicate it some for API writers, but don't you want them to be pretty capable anyway?  Shouldn't API writers be familiar enough with a language to take advantage of all the tools it provides?  The clients of such an API written to support closures wouldn't need to know any more than they would to take advantage of CICE in the vast majority of cases.

Contrast this complication with the Java Security Model.  Now, my understanding is that that thing is a mess (I'm sure necessarily so) that I wouldn't even take the time to understand unless I had a specific reason to do so.  That doesn't keep me from understanding Java at all, though.  Nor does it keep me from benefiting from some of the safety nets put in place by the JSM.  I think closures, as proposed in BGGA, are similarly complicated for a few but simple for many.

- J.

- J.


J. McConnell

未讀,
2007年3月21日 下午6:07:012007/3/21
收件者:java...@googlegroups.com
On 3/20/07, Alexey Zinger <inlin...@yahoo.com> wrote:
Call me dense, but I still don't understand.  His main problems with anonymous classes are:

Verbose, extra wordy (isn't that what verbose means?), clumsy, redundant -- syntax sugar.

Sure there's a dose of syntax sugar, but it does sweeten up a bunch of use cases, so might it be worth it?  Don't just dismiss it because it's syntax sugar.  The new for loop is just syntax sugar, but I don't see how that alone would have been a reason not to include it.

Incomplete capture of lexical context -- one man's explicit declaration of what free variables the anonymous class refers to is another man's incomplete capture of lexical context I guess.  I see nothing wrong with using EnclosingClass.this.foo.

As Neal points out, there are many more language constructs in Java that make up the lexical context than just variable names.  A good example is "The referent of a return statement."  He actually points out that the incorrect scoping of variable names is the least significant fault with anonymous inner classes, as far as them being closures goes.

Also, much of his pains with return types and exception throwing is just a matter of properly laying out your API.  Something we should strive for regardless.

What problems are these?  I couldn't find what you were referring you.

Control abstraction not possible -- yes it is.

No, it's not.

It's just a matter of how you lay out your API.  All he's saying is he doesn't wanna have to write generic enough interfaces that encompass exception handling and return types.

No, what he's saying is that he doesn't want to write interfaces at all, but rather the equivalent of language-level control constructs.

Here's an example:

I've created a BeanShell-based Java spreadsheet.  BeanShell is a Java syntax superset, so the BeanShell code will be obvious to everyone here.  A spreadsheet is by its very nature a kind of functional language environment, something that relies on closures heavily.  Immediately, I realized I had a need for a closure-like construct.  I did not want to much with the syntax if I could help it.  I did have a concept of cell ranges expressed as an iterator of their addresses.  A typical use of a closure here would be to express subrange selection in an each-member-such-that way.  I therefore created a conditional iterator that took a condition object.  All I had to do then was create an evaluated expression condition implementation that took a string value signifying some expression that got evaluated:

public class InterpretedCondition implements ConditionalIterator.Condition
{
    protected final BSHTableModel model;
    protected final String expression;

    public InterpretedCondition(BSHTableModel model, String expression)
    {
        this.model = model;
        this.expression = expression;
    }

    public boolean isSatisfied(Object objectValue)
    {
        try
        {
            Interpreter bsh = this.model.bsh;
            bsh.set("val", objectValue);
            Object result = bsh.eval(this.expression);
            return result != null
                && result instanceof Boolean
                && ((Boolean)result).booleanValue();
        }
        catch(EvalError ex)
        {
            ex.printStackTrace();
        }
        return false;
    }
}


And it's used like this ( [...] syntax is specific to the spreadsheet app; condition is a command that invokes a conditional iterator using the ):

sum(condition([c10:c200], "deref(val, 1, 0) != null && (deref(val, 1, 0).indexOf([-1-0]) > -1)"))

This statement adds numbers within the range of c10:c200 that are next to a cell ( deref(val, 1, 0) ) containing the text that's next to the cell we're in ( [-1-0] ).  Much the same code can be used within compiled Java itself, minus the special cell reference syntax of course.  We can throw the same exceptions.  We can return early.  We can do all the same stuff this guy is complaining about being difficult without changing the language.  I get the feeling he wants a functional language, which Java isn't.  But he can have things closer to functional languages thanks to the multitude of different dynamic languages available today that integrate with JVM.  I believe there's even a project for porting Haskel to JVM.  So I really don't see a need for modifying the Java language itself.

First off, let me say that this is fine code.  I don't have any problem with the code, but there's no control abstraction going on here.  What you aren't showing us here is the implementation of "sum", "condition" and "ConditionalIterator".  The "sum" method probably looks something like:

public double sum(Iterator iter) {
  double sum = 0;
  for (iter; iter.hasNext(); ) {
    Cell c = (Cell) iter.next();
    sum += c.getCellValue();
  }
  return sum;
}

What control construct does this code use?  It uses a "for" loop.  Why?  Because you don't have any other options in Java, so you need to fake out a conditional for loop by using a specialized Iterator.  What closures would give you is the ability to write this method as such:

public double sum(List<Cell> cells, Condition condition) {
  double sum = 0;
  forEachSuchThat(cells, condition) (Cell c) {
    sum += c.getCellValue();
  }
}

In essence here, you've created your own for loop that looks like and acts like a language-level control construct.  This is what Gafter is referring to as "control abstraction".

Check out his presentation here:

http://www.bejug.org/confluenceBeJUG/display/PARLEYS/Closures+for+Java

I didn't listen to the whole thing yet, but slides 12-17 seem pertinent to this discussion.

- J.

Werner Schuster (murphee)

未讀,
2007年3月21日 下午6:30:052007/3/21
收件者:java...@googlegroups.com
J. McConnell wrote:
> On 3/20/07, Alexey Zinger <inlin...@yahoo.com> wrote:
>
>> Verbose, extra wordy (isn't that what verbose means?), clumsy, redundant-- syntax sugar.

>>
> Sure there's a dose of syntax sugar, but it does sweeten up a bunch of use
> cases, so might it be worth it? Don't just dismiss it because it's syntax
> sugar. The new for loop is just syntax sugar, but I don't see how that
> alone would have been a reason not to include it

Simple answer for all Closure (lambda function, anonymous function, ...)
doubters:
Everyone who has ever asked for a new for_with_index construct in Java,
actually secretly wants Closures,
just doesn't know it yet.

The Java 5 new for Loop is a hack, that handles one single special case,
and quickly breaks down as soon as you want to do anything special.
Eg. map, fold, select, ... the same things suffixed with "_index", etc.
would all be possible _and_ nicely integrated with the language if
Closures were around.

There's this old argument that higher level [dynamic] languages had
problems because they weren't as suited towards refactoring... of
course, any language with closures supports many more refactorings than
Java.
Eg. this code
List results = new ArrayList();
for(Foo foo : results){
if(foo.foo()){
results.add(foo);
}
}

This sucker is rife for refactoring, since it's redundant. The only
significant code in there is the condition... the rest is boilerplate.
In the code review process, it'd be nice to refactor this code to
something concise like this:
results = foo.collect{|x| x.foo() }

No overhead, no code duplication, no source for errors stemming from
spreading similar code over your source base. Of course: only possible
in a language with Closures. (Yeah, you could approximate that with
Anonymous Functions, except that the code would then be littered with
loads of useless clutter).

And: the readability stays the same... simply because the implementation
of "collect" is just a Ctrl-Click away (assuming you use a proper IDE).
There ain't no magic, unlike language features, where implementation
semantics are hidden in the language spec/compiler.


Mind you: control flow or iteration constructs aren't the only use for
this,... once you got the idea of parameterizing algorithms, you'll see
opportunities everywhere.


murphee
-- Blog @ http://jroller.com/page/murphee Maintainer of EclipseShell @
http://eclipse-shell.sourceforge.net


Alexey Zinger

未讀,
2007年3月21日 晚上8:24:592007/3/21
收件者:java...@googlegroups.com
"J. McConnell" <jdo...@gmail.com> wrote:
On 3/20/07, Alexey Zinger <inlin...@yahoo.com> wrote:
Call me dense, but I still don't understand.  His main problems with anonymous classes are:

Verbose, extra wordy (isn't that what verbose means?), clumsy, redundant -- syntax sugar.

Sure there's a dose of syntax sugar, but it does sweeten up a bunch of use cases, so might it be worth it?  Don't just dismiss it because it's syntax sugar.  The new for loop is just syntax sugar, but I don't see how that alone would have been a reason not to include it.
The problem with syntax sugar is not that it lacks benefits.  It's that the benefits often don't outweigh the costs if we start sliding down the slippery slope of modifying core language with every release to accommodate specific usage patterns.  The new for loop is a very simple change that crosses nearly all conceivable usage patterns.  In my opinion, implementing closures within the language just doesn't get "everyone" enough over what can be done with anonymous instances and things like AspectJ.
Incomplete capture of lexical context -- one man's explicit declaration of what free variables the anonymous class refers to is another man's incomplete capture of lexical context I guess.  I see nothing wrong with using EnclosingClass.this.foo.

As Neal points out, there are many more language constructs in Java that make up the lexical context than just variable names.  A good example is "The referent of a return statement."  He actually points out that the incorrect scoping of variable names is the least significant fault with anonymous inner classes, as far as them being closures goes.
"Incorrect", being in the sense of language theory.  Language theory is really fun.  But in essence, it's useless both in the context of pragmatism as well as dreaming up new concepts.  When we talk about present and past technologies, fine, let's apply those methodologies.  But when we look into the future, there are only 2 angles: what crazy new concepts can we come up with and what will it be to actually use?  In both of those senses, what we know now or consider to be "correct" in isolation is irrelevant.  In other words, saying we need closures because current features of the language aren't true closures is a circular argument.  At least that's how I understand the situation.


Also, much of his pains with return types and exception throwing is just a matter of properly laying out your API.  Something we should strive for regardless.

What problems are these?  I couldn't find what you were referring you.
I must apologize -- I've been feeling a little under the weather lately.  I believe what I was referring to here is that Neal Gafter talks at great length about being able to return the right thing out of a closure.  See slide 15.  Maybe I'm not understanding correctly his point, but I see the issue as merely a question of correct API.


Control abstraction not possible -- yes it is.

No, it's not.
See below.


It's just a matter of how you lay out your API.  All he's saying is he doesn't wanna have to write generic enough interfaces that encompass exception handling and return types.

No, what he's saying is that he doesn't want to write interfaces at all, but rather the equivalent of language-level control constructs.
See below still...
For reference, here's ConditionalIterator and sum code:

public class ConditionalIterator implements Iterator
{
public static interface Condition
{
public boolean isSatisfied(Object contextValue);
}

protected final Iterator iterator;
protected final ConditionalIterator.Condition condition;

protected boolean notEnd = true;
protected Object nextValue;

public ConditionalIterator(Iterator iterator, ConditionalIterator.Condition condition)
{
this.iterator = iterator;
this.condition = condition;
this.fetchNext();
}

protected void fetchNext() throws NoSuchElementException
{
while(this.iterator.hasNext())
{
this.nextValue = this.iterator.next();
if(this.condition.isSatisfied(this.nextValue))
{
return;
}
}
this.notEnd = false;
}

public boolean hasNext()
{
return this.notEnd;
}

public Object next() throws NoSuchElementException
{
try
{
return this.nextValue;
}
finally
{
this.fetchNext();
}
}

public void remove() throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}
}
Ranges are actually represented by iterators of cell coordinates rather than cell values:
sum(iter)
{
sum = 0.0;
while(iter.hasNext())
{
val = iter.next();
model.requestDependency(val.x, val.y, col, row);
val = model.evalValueAt(val.x, val.y);
if(val != null && Number.class.isAssignableFrom(val.getClass()))
{
sum += val;
}
}
return sum;
}
What control construct does this code use?  It uses a "for" loop.  Why?  Because you don't have any other options in Java, so you need to fake out a conditional for loop by using a specialized Iterator.  What closures would give you is the ability to write this method as such:

public double sum(List<Cell> cells, Condition condition) {
  double sum = 0;
  forEachSuchThat(cells, condition) (Cell c) {
    sum += c.getCellValue();
  }
}
Yeah, and what's the difference other than verbosity (in setup, not much in client code)?  There's no performance hit either way.


In essence here, you've created your own for loop that looks like and acts like a language-level control construct.  This is what Gafter is referring to as "control abstraction".
Right.  And my point is that if it's possible to "fake" these control mechanisms already without performance hits or some other hidden issues, why do we need a change to the language?

Check out his presentation here:

http://www.bejug.org/confluenceBeJUG/display/PARLEYS/Closures+for+Java

I didn't listen to the whole thing yet, but slides 12-17 seem pertinent to this discussion.

- J.

J. McConnell

未讀,
2007年3月22日 上午11:13:592007/3/22
收件者:java...@googlegroups.com
On 3/21/07, Alexey Zinger <inlin...@yahoo.com> wrote:
"J. McConnell" <jdo...@gmail.com> wrote:
On 3/20/07, Alexey Zinger <inlin...@yahoo.com > wrote:
Call me dense, but I still don't understand.  His main problems with anonymous classes are:

Verbose, extra wordy (isn't that what verbose means?), clumsy, redundant -- syntax sugar.

Sure there's a dose of syntax sugar, but it does sweeten up a bunch of use cases, so might it be worth it?  Don't just dismiss it because it's syntax sugar.  The new for loop is just syntax sugar, but I don't see how that alone would have been a reason not to include it.
The problem with syntax sugar is not that it lacks benefits.  It's that the benefits often don't outweigh the costs if we start sliding down the slippery slope of modifying core language with every release to accommodate specific usage patterns.  The new for loop is a very simple change that crosses nearly all conceivable usage patterns.  In my opinion, implementing closures within the language just doesn't get "everyone" enough over what can be done with anonymous instances and things like AspectJ.

Alright, well I can't argue with your opinion :)  However, I would like to point out (as did murphee, I believe) that if Java had closures, the new for loop would have been unneccessary because you can implement the equivalent with closures.  So, there's one big usage right there.

Incomplete capture of lexical context -- one man's explicit declaration of what free variables the anonymous class refers to is another man's incomplete capture of lexical context I guess.  I see nothing wrong with using EnclosingClass.this.foo.

As Neal points out, there are many more language constructs in Java that make up the lexical context than just variable names.  A good example is "The referent of a return statement."  He actually points out that the incorrect scoping of variable names is the least significant fault with anonymous inner classes, as far as them being closures goes.
"Incorrect", being in the sense of language theory.  Language theory is really fun.  But in essence, it's useless both in the context of pragmatism as well as dreaming up new concepts.  When we talk about present and past technologies, fine, let's apply those methodologies.  But when we look into the future, there are only 2 angles: what crazy new concepts can we come up with and what will it be to actually use?  In both of those senses, what we know now or consider to be "correct" in isolation is irrelevant.  In other words, saying we need closures because current features of the language aren't true closures is a circular argument.  At least that's how I understand the situation.

No one is saying that we need closures because anonymous inner classes aren't closures.  People are saying we need closures because we want to do X, Y and Z, things like overhauling the Collections API to be much more usable.  The only reason the closure supporters are pointing out that anonymous inner classes aren't closures is because those that aren't as familiar with closures claim that they are.  In order to build support, it's necessary to point out this fallacy.

Also, much of his pains with return types and exception throwing is just a matter of properly laying out your API.  Something we should strive for regardless.

What problems are these?  I couldn't find what you were referring you.
I must apologize -- I've been feeling a little under the weather lately.  I believe what I was referring to here is that Neal Gafter talks at great length about being able to return the right thing out of a closure.  See slide 15.  Maybe I'm not understanding correctly his point, but I see the issue as merely a question of correct API.

No need to apologize, I've been sick myself.  First, Gafter isn't complaining about return types, he's complaining about the fact that the return statement loses it's semantics inside anonymous inner classes, I believe in an effort to point out a problem with CICE that BGGA solves.

It's funny that a couple of times you have taken mine of Gafter's using of "incorrect" and pointed out, more or less, that it just depends on your frame of reference and then you argue a point by say that it is "merely a question of a correct API."  Let's just say there's no such thing as a "correct" API.  However, it's true you can get the same exact functionality that Gafter is talking about by refactoring the API.  Of course you can!  Java is Turing-complete.  No one is arguing that.  All Gafter is saying is "Here's the code I want to write, here's why I can't write it in Java, as-is, here's how I could write it in Java with closures."  You can claim that the costs outweigh the benefits, but you can't claim that he can write what he wants without closures.

At this point I have a question.  Have you ever had any serious experience with closures?  If not, I can understand where you are coming from.  They don't seem like the biggest deal in the world until you've actually used them for any significant period of time.  For that reason, I feel this is an important question.

Control abstraction not possible -- yes it is.

No, it's not.
See below.
For reference, here's ConditionalIterator and sum code:
sum(iter)

{
sum = 0.0;
while(iter.hasNext())
{
val = iter.next();
model.requestDependency(val.x, val.y, col, row);
val = model.evalValueAt (val.x, val.y);
if(val != null && Number.class.isAssignableFrom(val.getClass()))
{
sum += val;
}
}
return sum;
}
In essence here, you've created your own for loop that looks like and acts like a language-level control construct.  This is what Gafter is referring to as "control abstraction".
Right.  And my point is that if it's possible to "fake" these control mechanisms already without performance hits or some other hidden issues, why do we need a change to the language?

There are a few reasons.  For one, not everything that you can do with closures can be as-easily faked out.  Also, this code is not as easy to read as code written with a custom forEachSuchThat loop construct.  The intention of the code is obscured somewhat by boilerplate.  The boilerplate in this example is small, but it is still there ( iter.hasNext(), iter.next(), etc.)  Other examples have much more boilerplate, much of which can be done away with using closures.

- J.

Alexey Zinger

未讀,
2007年3月22日 晚上7:18:382007/3/22
收件者:java...@googlegroups.com
--- "J. McConnell" <jdo...@gmail.com> wrote:

> It's funny that a couple of times you have taken mine of Gafter's using of
> "incorrect" and pointed out, more or less, that it just depends on your
> frame of reference and then you argue a point by say that it is "merely a
> question of a correct API." Let's just say there's no such thing as a

When I was talking about it being a matter of "correct API", I was referring
specifically to finding the right solution to the problem at hand, considering
specific abstractions and design patterns. However, when we talk about
introducing changes to the language, we are forced to think in global terms.

> "correct" API. However, it's true you can get the same exact functionality
> that Gafter is talking about by refactoring the API. Of course you can!
> Java is Turing-complete. No one is arguing that. All Gafter is saying is
> "Here's the code I want to write, here's why I can't write it in Java,
> as-is, here's how I could write it in Java with closures." You can claim
> that the costs outweigh the benefits, but you can't claim that he can write
> what he wants without closures.
>
> At this point I have a question. Have you ever had any serious experience
> with closures? If not, I can understand where you are coming from. They
> don't seem like the biggest deal in the world until you've actually used
> them for any significant period of time. For that reason, I feel this is an
> important question.

You're right. I have done minimal work with closures, all of it in Perl.
Being a long time Java coder, I have to admit that I did approach them as
shorthand for single-method anonymous classes even though the language itself
did not constrain me to that pattern. Still, I've made use of API and laid out
my own that lent themselves very easily to quite effective use of anonymous
instances (think Comparator), so I can't say I ever felt like having closures
would cause me to write better or substantially more readable code. I
understand the issue of flow control, but I can't say I'm seeing very many
useful patterns I'd take advantage of that would be different from we can do
now. Oh, and BTW, I'm not a particularly big fan of the new for loop either :)

> Control abstraction not possible -- yes it is.
> >
> >
> > No, it's not.
> >
> > See below.
> >
> For reference, here's ConditionalIterator and sum code:
> >
> > sum(iter)
> > {
> > sum = 0.0;
> > while(iter.hasNext())
> > {
> > val = iter.next();
> > model.requestDependency(val.x, val.y, col, row);

> > val = model.evalValueAt(val.x, val.y);


> > if(val != null && Number.class.isAssignableFrom(val.getClass()))
> > {
> > sum += val;
> > }
> > }
> > return sum;
> > }
> >
> > In essence here, you've created your own for loop that looks like and acts
> > like a language-level control construct. This is what Gafter is referring
> > to as "control abstraction".
> >
> > Right. And my point is that if it's possible to "fake" these control
> > mechanisms already without performance hits or some other hidden issues,
> why
> > do we need a change to the language?
> >
>
> There are a few reasons. For one, not everything that you can do with
> closures can be as-easily faked out. Also, this code is not as easy to read
> as code written with a custom forEachSuchThat loop construct. The intention
> of the code is obscured somewhat by boilerplate. The boilerplate in this

> example is small, but it is still there (iter.hasNext(), iter.next(), etc.)


> Other examples have much more boilerplate, much of which can be done away
> with using closures.

If I need to be able to daisy-chain my conditional subrange statements without
forcing evaluation of the range to its end if the outermost control loop
terminates early, how would I be able to substantially reduce boilerplate code
here even with use of closures?


____________________________________________________________________________________
The fish are biting.
Get more visitors on your site using Yahoo! Search Marketing.
http://searchmarketing.yahoo.com/arp/sponsoredsearch_v2.php

reinierz

未讀,
2007年3月23日 清晨7:37:572007/3/23
收件者:The Java Posse
The concept of Closures having a name and being the talk of the python/
ruby/haskell/lisp fanboys is certainly no reason to include them in
the JRE!

Closures allow you to solve certain problems in an elegant and/or
maintainable fashion and the abilities to solve those problems are the
reason to include them in the next release of java.

I claim that CICE closures allow you to solve 95% of all use cases
that would make a functional programmer go: I know! I'll use a
closure!

And THAT is the reason why CICE is so much better than gafter. If
Gafter claims CICE (read: anonymous classes, CICE is just syntactic
sugar for them) aren't true closures, I understand and even agree.
However, there are so many things you can do with closures that you
can do equally well with anon classes, and anon classes are 'the java
way'. In a big way, let me explain:

java.util.Comparator<Integer>, in BGGA land, would become:

int(int, int)

however, this is worthless type info. Of the caliber where python and
ruby enthusiasts correctly claim the hassle of the logistics are not
worth the benefits of avoiding ClassCastExceptions, because those
never happen. The point of static typing is to give a concept a full
(namespaced) name, and a place for javadocs, so that you can have
those pop up and refer to them right from inside the code at any time.

int(int, int)? What the heck is that? It doesn't even look like
Comparator. As far as I know, that seems to be the type of a function
that can be assigned to a calculator application's operator buttons
(+, -, *, / and the like).

In CICE you would write:

Comparator(int a, int b) { return Math.abs(a) - Math.abs(b); } //
Comparator is a single-abstract-method interface or abstract class, so
we can use CICE. Return type, throws clause, and any annotations are
taken from Comparator's declaration (e.g. the compiler knows compare()
returns an int, so this pseudomethod also will)...

and yet this thing is fully typed. It's a COMPARATOR, that sounds
logical (in fact, It's a Comparator<Integer>) and I have a place to
refer to javadocs for it.

That's the java way. You may not think this way is always appropriate,
but that's what JRuby and Groovy are for.

Allowing BGGA closures into java turns java into a balls-less ruby. No
thanks. I actually like static typing. A language can't be everything
to everybody (people have tried with e.g. PL/I. Ever see any PL/I-
based projects around anymore these days?) so java took it to the next
step and created a JVM that could be everything to everybody. So far
that theory is either working or has yet to prove itself, depending on
who you speak to, though the popularity of groovy seems to indicate
that its working.

On Mar 21, 3:48 pm, "J. McConnell" <jdo...@gmail.com> wrote:

J. McConnell

未讀,
2007年3月23日 上午8:31:552007/3/23
收件者:java...@googlegroups.com
On 3/23/07, reinierz <rein...@gmail.com> wrote:

java.util.Comparator<Integer>, in BGGA land, would become:

int(int, int)

int(int, int)? What the heck is that? It doesn't even look like
Comparator. As far as I know, that seems to be the type of a function
that can be assigned to a calculator application's operator buttons
(+, -, *, / and the like).

In CICE you would write:

Comparator(int a, int b) { return Math.abs(a) - Math.abs(b); }

This isn't a fair comparison.  While BGGA does have function types, so {int, int => int} is in fact a type, BGGA also allows for something like:

Comparator c = { int a, int b => Math.abs(a) - Math.abs (b); };

As with CICE, single method abstract classes or interfaces can be instantiated with this kind of short-hand.

- J.

J. McConnell

未讀,
2007年3月23日 上午8:38:032007/3/23
收件者:java...@googlegroups.com
On 3/22/07, Alexey Zinger <inlin...@yahoo.com> wrote:

You're right.  I have done minimal work with closures, all of it in Perl.

Present discussion aside, I really would recommend finding some time to work with a language that supports them well.  Ruby or lisp would both be good choices.  Regardless of whether they end up in Java or not, it will make you a better coder.

Oh, and BTW, I'm not a particularly big fan of the new for loop either :)

Lol, at least you're consistent :)

> For reference, here's ConditionalIterator and sum code:
> >
> > sum(iter)
> > {
> >  sum = 0.0;
> >  while(iter.hasNext())
> >  {
> >   val = iter.next();
> >   model.requestDependency(val.x, val.y, col, row);
> >   val = model.evalValueAt(val.x, val.y);
> >   if(val != null && Number.class.isAssignableFrom(val.getClass()))
> >   {
> >    sum += val;
> >   }
> >  }
> >  return sum;
> > }
> >
> There are a few reasons.  For one, not everything that you can do with
> closures can be as-easily faked out.  Also, this code is not as easy to read
> as code written with a custom forEachSuchThat loop construct.  The intention
> of the code is obscured somewhat by boilerplate.  The boilerplate in this
> example is small, but it is still there (iter.hasNext(), iter.next(), etc.)
> Other examples have much more boilerplate, much of which can be done away
> with using closures.

If I need to be able to daisy-chain my conditional subrange statements without
forcing evaluation of the range to its end if the outermost control loop
terminates early, how would I be able to substantially reduce boilerplate code
here even with use of closures?

To be clear, the only boilerplate I was talking about in this example are two lines:

while (iter.hasNext()) {
  val = iter.next();

Those could be done away with using a closure.  Admittedly, that doesn't make much of a difference in this example, but you know you've written a lot of boilerplate in your life in other scenarios ;)

- J.

reinierz

未讀,
2007年3月24日 上午10:38:122007/3/24
收件者:The Java Posse
>
> Comparator c = { int a, int b => Math.abs(a) - Math.abs(b); };
>


Also unfair.

In this case the Comparator c = prefix makes abundantly clear what's
going on here.

However, the type of this:

{ int a, int b => Math.abs(a) - Math.abs(b); };

is (int, int) => int, and NOT comparator!

whereas the CICE equivalent:

Comparator(int a, int b) { return Math.abs(a) - Math.abs(b); };

*IS* specified (Comparator<Integer>) without the need to look up where
it's going. This is important, because in the current release of java,
it is impossible to carry a half-bound type around that will
implicitly become whatever is requested. The closest equivalent is
carrying a supertype (or even an Object) around, which gets cast to
the 'right' thing at some later point in time. This works, but it's 1)
ugly, and 2) everyone tells you not to do this, and there's a good
reason. That Isn't Java. That's way more suited to groovy, scala, or
jruby. I again repeat: if this is important to you, if your brain
prefers to program in this manner, why aren't you using those
languages instead?

The reason it's bad IS for refactoring. I know someone in this thread
mentioned that adding BGGA closures in fact offers more refactoring
opportunities and then showed an example of a standard function-on-
collection operation, which does absolutely nothing to prove that
point ,which I'm fairly sure is dead wrong. Perhaps we are confusing
terminology; refactoring, as in, the compiler (read: whatever you use
to write code with, e.g. eclipse, IDEA, or netbeans - assuming we're
all working with a notepad with a lot of sugar is unfair, java sucks
if you do that, this is known and not something that warrants fixing)
can figure out exactly what code fragments you are referring to given
a refactoring op. If I want to rewrite all 'comparators' then the
refactoring will not be complete as in some code fragments, we have an
(int, int) => int that is secretly a comparator. Refactoring is a
borderline use-case, but reconsider this same line of reasoning but
this time simply with: Find me all places where I have comparators in
the first place.

The crucial thing here is: In the current situation, an object
intended as a Comparator (or any other type) STARTS as that type (The
operation of constructing an object is virtually always typed;
constructors are forced, and factories that just return Object are
horribly designed) and also ENDS at that type (e.g. you can't toss an
Object into Collections.sort(a, (comparator)) and have it silently
cast to Comparator; you must be explicit about such things). In CICE
land, as the language doesn't change in any way or form (it's just
sugar) this is no different, but in BGGA land it is; they end as an
implicitly casted Comparator, and start life as an (int, int) => which
is only half a type;

it's the syntactic notion of type (e.g. an implicit BGGA cast from
(int, int) => to Comparator cannot throw a ClassCastException because
syntactically, this 'fits', guaranteed), but it's missing the semantic
notion of type: While a Comparator<Integer> and a
CalculatorButtonOperation are ENTIRELY DIFFERENT CONCEPTS, as
different as night and day, really, by pure coincidence they share the
same syntatic function type: (int, int) => int for the both of them.
The fact that this can create bugs is only a minor issue, just like
the fact that static typing moves 'class cast exceptions' from runtime
to write/compile time is only a minor issue: The real point is that
while programming you see a lot of types while using other people's
(or API's) code, and types serve as implicit, checked,
'documentation'. (int, int) => int is in many situations not a lot of
help when sussing out what something does, and there is no opportunity
for javadocs.

CalculatorButtonOperation does, as does Comparator. Thus, I declare
that any creation of a functor (CICE/java5: An anonymous class, BGGA:
a closure), unless it's a massive amount of effort, having an
explicitly named fully namespaced type is always better; the shortcut
of not supplying one is not for java; that's the strength (and
weakness) of other languages. Introducing this to java is making java
into a wimpy version of ruby or python. I'd rather have .... ruby or
python! - and with JRuby around and the recent resurgence in jython,
there is absolutely no reason to move java into that direction.

Unfortunately right now it IS a lot of effort, which is where CICE
comes in: Syntactic sugar to reduce the effort. In fact, my code is
littered with stuff like this:

Runnable hook = new Runnable() { public void execute() {\n
doSomething();\n
}};

where '\n' means: newline in my code. It's a syntax hack to represent
that there's really only one conceptual 'level' of indentation going
on here, not two. CICE addresses exactly that, and nothing more. Java
really owes trying this concept at the very least before unleashing
the havoc of BGGA, just to see if this will cover enough ground. I'm
in fact sure it will, but I can imagine opinions differ.

As a final note, consider that with CICE closures, most 'function-over-
collection' operations become elegant and simple as well, with the
right API support. I present you the CICE-ified operations right here:
http://www.zwitserloot.com/2006/11/25/closures-yes-java-has-them/

Includes select, python do/with, and others. Note how in all
circumstances some light API modifications and CICE makes them look
good and act elegant.

回覆所有人
回覆作者
轉寄
0 則新訊息