The Artima Developer Community
Sponsored Link

Weblogs Forum
Closures and Anonymous Functions

54 replies on 4 pages. Most recent reply: Sep 3, 2006 6:09 AM by Jules Jacobs

Welcome Guest
  Sign In

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

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Closures and Anonymous Functions (View in Weblogs)
Posted: Aug 22, 2006 12:06 PM
Reply to this message Reply
Summary
There is a debate raging at the Lambda-the-Ultimate.com site about closures in Java. Here's my two cents on the subject.
Advertisement
The debate at http://lambda-the-ultimate.org/node/1684 is essentially whether closures are needed in Java or not. I have an issue with the debate: a lot of people are arguing for anonymous function, not closures.
  • A closure isn't neccessarily anonymous.
  • An anonymous function doesn't neccessarily capture the lexicial environment
While I completely agree that anonymous functions are very useful for certain styles of programming, closures have a very clear downside: they increase code coupling. Passing a single closure can extend the lifetime of massive numbers of objects, leading to a huge performance hit.

Closures are a very powerful feature, but dangerously so. They are arguably too easily abused. A language designer has the responsibility to force a programmer to be explicit about dangerous things.

I am not saying that there is any single right answer for all cases, but I do think that people need to make a clear case why closures are important to have versus anonymous functions.


James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Closures and Anonymous Functions Posted: Aug 22, 2006 3:09 PM
Reply to this message Reply
One of the big arguments I see there is whether anonymous inner classes (and presumably local classes) are basically a form of closure or not. The restriction on accessing only finals seems to me to just be a technical compromise to allow the values to be copied into the new Object.

Your post here implies that you believe there is a distinction. Can you explain to me what it is? I feel that I am missing something. Thanks.

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Closures and Anonymous Functions Posted: Aug 22, 2006 4:09 PM
Reply to this message Reply
Code inside anonymous inner classes can't modify the lexical environment, it can only read.

Why you need closures:

function scale_list(list, scale_factor){
  return map(list, function(e){return e * scale_factor}
}


I think it's bad for language designers to think "this is easily abused, don't allow it". A better way of thinking is: "this is easily abused, but programmers who program in this language are smart, so they won't abuse it". Besides, if you have a not-dumb compiler, the program can garbage collect most variables which can't be referenced in the closure.

For example:

function foo(a){
  b = a * 2;
  return bar(function(i){i + b})
}


You can safely garbage-collect a here, because it's not used in the closure. You don't have to remember more data than in a language which doesn't have closures. The scale_list example:

function scale_list(list, scale_factor){
  newlist = []
  for(e in list){
    newlist.append(e * scale_factor)
  }
  return newlist
}


Now you need to remember the scale factor until the function terminates too.

Fortunately, this is a non problem in Cat because you can't have closures because there are no lexical environments. Currying can be used instead:

scale_list = [*] curry map
 
example:
 
{1,2,3} 2 scale_list =>
{1,2,3} 2 [*] curry map =>
{1,2,3} [2 *] map =>
{2,4,6}


Note that curry is the same as cons in Joy.

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

Language design Posted: Aug 22, 2006 6:31 PM
Reply to this message Reply
"A better way of thinking is: 'this is easily abused, but programmers who program in this language are smart, so they won't abuse it.'"

Or, perhaps, "this can be bad when XXX, or can be dangerous because YYY, therefore we will ZZZ." Why? Because given a choice between having the programmer's smarts save the day and having the compiler keep track of the details, I'll pick the compiler. I want the feature, but I don't want to be the one to keep things straight.

For instance, people can learn multi-threading in 100-line toy programs, but then they have serious trouble keeping things straight when the program grows to something more realistic. Given a choice between a langauge that says "our programmers are smart enough to keep track of details" and a compiler that keeps track of those details for me, I will likely pick the compiler (so long as I can override the compiler if it's ever wrong).

And how would the compiler keep track of the details? In another thread, I suggested a strongly typed language can guarantee that a variable is locked before access, and unlocked as many times as it's been locked by (1) creating only unlocked_access_variables, (2) defining a conversion between unlocked_access_variables and locked_access_variables that does the necessary locking/unlocking, and (3) defining all functions to take locked_access_variables.

That doesn't solve deadlock, which is where the language/compiler comes in. The language/compiler can guarantee that a variable is locked in the same order every time regardless of which order the programmer passes it to a function (for instance, by building up a list of variables that a function wants to lock, ordering that list based on some invariant that the compiler knows about {perhaps a unique ID that is assigned to each object upon creation}, and then locking in that order). This would (1) free me from keeping track of a detail, (2) at least not prevent me from locking things by hand if I wanted to, and (3) also not prevent me from using various lock-free techniques if I wanted that as well.

Cleo Saulnier

Posts: 77
Nickname: vorlath
Registered: Dec, 2005

Re: Closures and Anonymous Functions Posted: Aug 22, 2006 8:18 PM
Reply to this message Reply
C.D.: Very well said!

I don't mind Anonymous Functions. Not sure about Java, but they're used all the time with callbacks. Borland's closures in C++ are just an instance and member pointer pair. I don't mind these things so much.

But from an implementation point of view, I hate what real closures do to my stack. Stack space is a problem enough. Duplicating them isn't my idea of fun.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Closures and Anonymous Functions Posted: Aug 22, 2006 8:46 PM
Reply to this message Reply
> Code inside anonymous inner classes can't modify the
> lexical environment, it can only read.
>
> Why you need closures:
>
> function scale_list(list, scale_factor){
> return map(list, function(e){return e * scale_factor}
> }

Can you explain to me how the above is fundamentally different from the following?

public class Test
{
    public static void main(String args[])
    {
        
    }
 
    List scaleList(List<Integer> in, final int scale_factor)
    {
        return Functional.map(in, new Function<Integer, Integer>() {
            public Integer exec(Integer i) { return i * scale_factor; }});
    }
}
 
interface Function<E,V>
{
    V exec(E in);
}
 
class Functional
{
    public static <E,V> List<V> map(List<E> list, Function<E, V> f)
    {
        List<V> out = new ArrayList<V>();
 
        for (E item : list) {
            out.add(f.exec(item));
        }
 
        return out;
    }
}

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Closures and Anonymous Functions Posted: Aug 22, 2006 8:52 PM
Reply to this message Reply
> I don't mind Anonymous Functions.

I do, BTW, very much want anonymous functions in Java. The versbosity of the example above is a good example of why.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Closures and Anonymous Functions Posted: Aug 22, 2006 11:44 PM
Reply to this message Reply
I have logged a request for an enhancement to Java for shorter syntax for common operations. One of these common operations is anonaymous inner classes. Which as many people have pointed out have much in common with closures and first class functions. The aim of the proposed syntax changes are to remain backwards compatible with existing Java and provide a natural progression towards shorter syntax.

You can vote for and see detail of the proposal at:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6389769

For example, instead of the current:
textField.addActionListner( new ActionListener() {
    public void actionPerformed( ActionEvent notUsed ) {
        textArea.append( textField.getText() );
    }
});

You could write with my proposal:
textField.addActionListner( ActionListener() ( notUsed ) textArea.append( textField.getText() ); );

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Closures and Anonymous Functions Posted: Aug 23, 2006 9:08 AM
Reply to this message Reply
> I have logged a request for an enhancement to Java for
> shorter syntax for common operations. One of these common
> operations is anonaymous inner classes. Which as many
> people have pointed out have much in common with closures
> and first class functions.

Can you explain what they don't have in common with closures and first class functions. I still haven't found a clear answer to that question.

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Closures and Anonymous Functions Posted: Aug 23, 2006 11:30 AM
Reply to this message Reply
> For instance, people can learn multi-threading
> in 100-line toy programs, but then they have
> serious trouble keeping things straight when the
> program grows to something more realistic.
> Given a choice between a langauge that says "our
> programmers are smart enough to keep track of details"
> and a compiler that keeps track of those details for
> me, I will likely pick the compiler (so long as I can
> override the compiler if it's ever wrong).

I don't think that programmers should keep track of the details. You can abstract these things in a good programming language (with message passing style concurrency for example). But I do think that you shouldn't make things impossible that could be confusing but potentially useful.

Continuations for instance are really confusing. Continuations are great for writing incomprehensible programs, but they are also very useful for web applications.

> Can you explain to me how the above is fundamentally
> different from the following?
> [...]

The first is 3 lines, the second is 32 lines.

You can always do the same things in turing equivalent languages. The difference is *how* you do it, and most important: how much effort it will take.

> Can you explain what they don't have in common
> with closures and first class functions. I still
> haven't found a clear answer to that question.

Anonymous inner classes can't modify the environment (without hacks).

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Closures and Anonymous Functions Posted: Aug 23, 2006 11:41 AM
Reply to this message Reply
> The first is 3 lines, the second is 32 lines.

That's not what I would consider a fundamental difference. In short you did not provide a helpful answer. You gave an example of something that can be done in Java. That it takes more code is not pertinent nor is it interesting. I pointed out that it was verbose myself.

You also seem to assume that I am saying that no change should be made and that is not the case at all.

> You can always do the same things in turing equivalent
> languages. The difference is *how* you do it, and most
> important: how much effort it will take.

Thanks but I already took CS101.

> > Can you explain what they don't have in common
> > with closures and first class functions. I still
> > haven't found a clear answer to that question.
>
> Anonymous inner classes can't modify the environment
> (without hacks).

The fact that you have to qualify this statement demonstrates that this is also not a fundamental difference.

It seems to me that a simple solution that wouldn't require major changes to the language would be to add implicit interfaces (kind of the way arrays work in Java) to support new cleaner syntax. What I am trying to determine is whether this is enough or if there is something else that would be required to make these true closures. Your reponses are not anywhere near this mark.

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

language design Posted: Aug 23, 2006 1:27 PM
Reply to this message Reply
[blockquote]I don't think that programmers should keep track of the details. You can abstract these things in a good programming language (with message passing style concurrency for example). But I do think that you shouldn't make things impossible that could be confusing but potentially useful.[/blockquote]

Today I ran across Effective C++ in the library, and read a little about how it was to use C++ without the typeof operator, which was originally left out because it could be easily abused. Generally, programmers who asked for typeof really only needed to get more comfortable with virtual functions and OOP principles. However, there were some programmers that actually needed typeof, and it wasn't hard to "fake it." Problem was that so many people faked it in so many ways that the "cure" of leaving out typeof was worse than the disease.

So, re-reading the original comment that I responded to, my comment, and the response, I have to say (1) I misunderstood what you originally said, and (2) I agree wholeheartedly with what you've been saying, and (3) I *also* believe the compiler should try to keep down complexity.

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

Verbosity in language design Posted: Aug 23, 2006 2:01 PM
Reply to this message Reply
> > The first is 3 lines, the second is 32 lines.

> That's not what I would consider a fundamental difference. In short you did not provide a helpful answer. You gave an example of something that can be done in Java. That it takes more code is not pertinent nor is it interesting. I pointed out that it was verbose myself.

Lemme wade in here and say that Java's verbosity is my biggest hangup with the language. If it takes ten times the LOC to solve a problem in Java than in (insert language here), then I can expect to spend a lot of my career creating syntactic scaffolding. So, yes, to me, the fact that Java requires not just more code, but ten times the LOC is pertinent to me. I don't like spinning my wheels on my car, and I don't like spinning my wheels on a programming project either.

More in-depth discussion at Stevey's Drunken Blog Rants: http://opal.cabochon.com/~stevey/blog-rants/lisp-wins.html :

"So mostly I'm a Java programmer.

"Initially it was pretty much a six-year-long love affair. Of course, after 5 years of assembly language, anything with if-statements and loops would have seemed like heaven. But half a million lines of code, most of it in one application, is enough to ruin the best of honeymoons. I realized, about 18 months ago, that I was getting sick of Java. ...

"You can only go so far with Java's OO model before you've stripped it down to what I'd call 'essential boilerplate code'. Every language has some amount of it: code that doesn't affect the computation, and only exists (presumably) to help the compilers who were at the bottom of their class in Compiler School. Java, in my opinion, has waaay too much boilerplate that you can't eliminate, and it obscures your actual application logic and slows down development, refactoring, debugging, and just about everything else.

"Part of the problem is just clutter: the Java programming language is pretty darn verbose. ..."

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Verbosity in language design Posted: Aug 23, 2006 2:44 PM
Reply to this message Reply
> Lemme wade in here and say that Java's verbosity is my
> biggest hangup with the language. If it takes ten times
> the LOC to solve a problem in Java than in (insert
> language here), then I can expect to spend a lot of my
> career creating syntactic scaffolding.

First of all the 32 to 3 figure is obviously nonsense because the 32 count includes the map() implementation and the 3 does not. In addition the 32 figure counts an empty main method and a Test class declaration.

If you want to count apples to apples, here are the lines of Java:
List scaleList(List<Integer> in, final int scale_factor)
    {
        return Functional.map(in, new Function<Integer, Integer>() {
            public Integer exec(Integer i) { return i * scale_factor; }});
    }
 
interface Function<E,V>
{
    V exec(E in);
}


That's 9 lines including blanks and single brace lines. If we don't count those we get: 5 lines. We could remove break put in there for readability and we get 4 lines.

Who cares? It doesn't have the anything to do with my question. Why is this so hard to understand?

> So, yes, to me,
> the fact that Java requires not just more code, but ten
> times the LOC
is pertinent to me.

I didn't say it wasn't pertinent. Can someone please answer the following question:

Can you do things with closures that you cannot do with anonymous inner classes or not? Are we just talking about reducing verbosity of something more?

I'm aware of (at least some of) what approaches closures allow. I agree that anonymous inner classes are too verbose. These are facile points that I am not interesting in discussing. So far the answer has been 'less lines'. Of course there are less lines. Who wouldn't realize that?

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Verbosity in language design Posted: Aug 23, 2006 7:19 PM
Reply to this message Reply
If a functional type collection library where added to Java then the example would become:
FList scaleList( final FList<Integer> in, final int scale_factor ) {
    return in.map( new F<Integer, Integer>() {
        public Integer e( final Integer i ) {
            return i * scale_factor; 
        }
    } );
}

If you add the syntax simplifications that I propose:
FList scaleList( final FList<Integer> in, final int scale_factor )
    return in.map( F<Integer, Integer>() ( final i )
        return i * scale_factor; 
    );

Not bad :)

Flat View: This topic has 54 replies on 4 pages [ 1  2  3  4 | » ]
Topic: Closures and Anonymous Functions Previous Topic   Next Topic Topic: Frustration-Driven Language Development


Sponsored Links



Google
  Web Artima.com   

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