The Artima Developer Community
Sponsored Link

Weblogs Forum
Closures and Anonymous Functions

54 replies on 4 pages. Most recent reply: Sep 3, 2006 3: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 | » ]
Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Verbosity in language design Posted: Aug 25, 2006 12:30 AM
Reply to this message Reply
Advertisement
> But actually, I think there may be a distinction. With an
> anonymous function or closure in a lot of languages, can't
> you return an anonymous function with a return type of int
> (for example) in place of an int. In effect delaying the
> execution of the function until the return value is used
> or something? That's something you definitely can't do in
> Java.

You can't do that in any language I know.

In Scheme for example:

(+ 2 4)
=>
6
 
(+ 2 (lambda () 4))
=>
ERROR


Because of Haskells laziness the behavior you describe is the default for all function calls. That's why you can declare a list of all integers, then square all numbers in this list. You can't output the list (well, you can, but that would take forever). You can take the first 5 numbers (0 1 4 9 16), and the squaring will be executed when you take these numbers (so not when you define the list).

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Verbosity in language design Posted: Aug 25, 2006 5:57 AM
Reply to this message Reply
> > But actually, I think there may be a distinction. With
> an
> > anonymous function or closure in a lot of languages,
> can't
> > you return an anonymous function with a return type of
> int
> > (for example) in place of an int. In effect delaying
> the
> > execution of the function until the return value is
> used
> > or something? That's something you definitely can't do
> in
> > Java.
>
> You can't do that in any language I know.

I think you can do it in Python but (if it does let you) it's because of dynamic typing not the closures, I suppose.

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

Language design Posted: Aug 25, 2006 7:28 AM
Reply to this message Reply
I haven't done a great job explaining exactly what I'm commenting on, and what I'm not commenting on, so I'm not upset that people haven't guessed the parameters I've arbitrarily and implicitly chosen.

/* Java should have had multi-methods from the start. It's what most (in my experience) developers expect and you can don't have to use the feature.
*/

The more I consider language design, the more attracted I am to good metaprogramming systems.

For instance, I'm tempted to use a garbage collecter every so often, but I know that garbage collectors have drawbacks (mainly, you can't guarantee when an object will be collected, so you generally can't guarantee when the finalizer will be run, so you can't use the finalizer to run code that needs to be run at a specific point in time). The language has to give a strategy for memory management and resource management. Perhaps a language could (1) provide a garbage collector automatically, (2) make it possible to turn that collector off if you need to, and (3) provide facilities that make it possible to write (a) a different garbage collector, and (b) smart pointers, or other memry/rsource management techniques.

Likewise, a language has to say "our object system is. ..." For most programmers that solves the puzzle. I'd probably never need to go beyond that myself. But *if* I ever did, it sure would be nice if the language said "... and here are a few hooks/tools/building blocks so that you can create different kinds of obbject/type systems when necessary." You'd want these custom objects and types to look different than the generic versions, so that it'd be easy to keep things straight.

Perl 5 doesn't come with AOP out-of-the box, but it does give a few hooks into the symbol table (typeglobs and the "magic goto" mainly, except that the magic goto's been discontinued http://search.cpan.org/~rgarcia/perl-5.9.3/pod/perl593delta.pod#Magic_goto_and_eval ). It's possible to hijack a call to a subroutine, do whatever you want and then say "goto &sub" to go ahead and do what the sub was supposed to do. This gives you some AOP capabilities, if you ask for them.

And, yes, I know Lisp programmers say Lisp has all the metaprogramming features you could ever want. Unfortunately I haven't been able to get excited enough abut Lisp to actually use it. Take that for what you think it's worth.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Language design Posted: Aug 25, 2006 8:52 AM
Reply to this message Reply
> For instance, I'm tempted to use a garbage collecter every
> so often, but I know that garbage collectors have
> drawbacks (mainly, you can't guarantee when an object will
> be collected, so you generally can't guarantee when the
> finalizer will be run, so you can't use the finalizer to
> run code that needs to be run at a specific point in
> time). The language has to give a strategy for memory
> management and resource management. Perhaps a language
> could (1) provide a garbage collector automatically, (2)
> make it possible to turn that collector off if you need
> to, and (3) provide facilities that make it possible to
> write (a) a different garbage collector, and (b) smart
> pointers, or other memry/rsource management techniques.

My opinion is that in Java (at least) the correct solution is to use... wait for it... closures.

One of the biggest issues with API designs in Java is that that they are based on the Iterator principle. You have this Object that you hand to someone and they keep saying "give me another" and you give them another. The problem with this is basically what you describe above. When can you clear the resources. You generally can't know when. You have to wait until the GC comes along marks it as garbage. That could be a minute later, it could be a 2 hours later. It could be never because the client never let go. A great example of this is how JDBC is designed. You call a resultset until you decide you are done. Then you are supposed to clean-up. So unless you deal with this up-front, you'll have a lot of duplicated cleanup code and it's (in reality) vendor specific.

But consider if JDBC didn't use the Iterator pattern. Let's say instead, the client passed in a RowHandler Object and the JDBC implementation called it until it said 'when' and cleaned up as it needed to. Because you never reliquish control of the resource, you can manage it effectively without depending on the garbage collector.

Another related example is the Iterator returned from a collection. If you want to wrap the collection in a thread-safe wrapper, you'd better read the disclaimer that says, "oh yeah, anyone who calls iterator needs to synchronize explicitly". Basically, if the client doesn't know or care whether the collection is thread-safe, we are screwed. Again, this is not a problem if you instead use a closure style and pass a handler to the collection.

These are the main reasons I think it's crucial to lower the bar for using closures (really anonymous inner classes) and function pointers in Java.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Language design Posted: Aug 25, 2006 8:53 AM
Reply to this message Reply
> Perl 5 doesn't come with AOP out-of-the box, but it does
> give a few hooks into the symbol table (typeglobs and the
> "magic goto" mainly, except that the magic goto's been
> discontinued
> http://search.cpan.org/~rgarcia/perl-5.9.3/pod/perl593delta
> .pod#Magic_goto_and_eval ). It's possible to hijack a
> call to a subroutine, do whatever you want and then say
> "goto &sub" to go ahead and do what the sub was supposed
> to do. This gives you some AOP capabilities, if you ask
> for them.
>
> And, yes, I know Lisp programmers say Lisp has all the
> metaprogramming features you could ever want.
> Unfortunately I haven't been able to get excited enough
> h abut Lisp to actually use it. Take that for what you
> think it's worth.

Have you looked at Scala?

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

Re: Language design Posted: Aug 25, 2006 10:36 AM
Reply to this message Reply
/* Have you looked at Scala?
*/

To be honest, I looked at it somewhat half-heartedly. I took a long look at OCaml, but just goodn't get comfortable with the syntax. I'm weighing Scala vs. Haskell right now, but not urgently.

/* I've often seen is stated that anonymous functions are the 'closests things to closures' in Java and I've yet to find a good explanation as to why they were just close. I guess I never considered that verbosity would be the only difference because that seems like a pretty superficial qualification. I mean I would find it odd to find a formal definition of closure that included something about how much code was required to create one.
*/

I had a strange response to this written up before I realized it boils down to what Diggins wrote in the original post:

* A closure isn't neccessarily anonymous.
* An anonymous function doesn't neccessarily capture the lexicial environment

The definition of closure I'm familiar with is "a function that captures the lexical environment for later use." You can use objects to create closures, and you can use closures to create objects. However, while objects *can* capture the lexical environment, they don't have to. And closures usually don't come with built-in inheritance relationships.

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

that's a new typo Posted: Aug 25, 2006 10:40 AM
Reply to this message Reply
"goodn't" should be "couldn't." I only bring that up because it's not a normal typo like "teh" or "recvve" or something like that.

Todd Blanchard

Posts: 316
Nickname: tblanchard
Registered: May, 2003

Re: Language design Posted: Aug 25, 2006 11:35 AM
Reply to this message Reply
I'm tempted to use a garbage collecter every so often, but I know that garbage collectors have drawbacks (mainly, you can't guarantee when an object will be collected, so you generally can't guarantee when the finalizer will be run, so you can't use the finalizer to run code that needs to be run at a specific point in time). The language has to give a strategy for memory management and resource management. Perhaps a language could (1) provide a garbage collector automatically, (2) make it possible to turn that collector off if you need to, and (3) provide facilities that make it possible to write (a) a different garbage collector, and (b) smart pointers, or other memry/rsource management techniques.

Or use closures. In Smalltalk:

aDatabase inTransactionDo: [...a bunch of updates...]

the implementation of inTransactionDo: aClosure will open a transaction, set up an exception handler, invoke the closure, then close the transaction. The exception handler will abort the transaction and propagate the error.

This eliminates a tremendous amount of boilerplate like you have in Java with try and finally blocks everywhere.

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Language design Posted: Aug 25, 2006 11:58 AM
Reply to this message Reply
> (mainly, you can't guarantee when an object will
> be collected, so you generally can't guarantee when the
> finalizer will be run, so you can't use the finalizer to
> run code that needs to be run at a specific point in
> time).

The solution is of course to have a stateless programming model, like Haskell. There is no "time", so you cannot (nor need) to depend on it.

> And, yes, I know Lisp programmers say Lisp has all the
> metaprogramming features you could ever want.
> Unfortunately I haven't been able to get excited enough
> abut Lisp to actually use it. Take that for what you
> think it's worth.

Watch this: http://lispm.dyndns.org/mov/dsl-in-lisp.mov

Lisp style is mostly functional style. The list is the primary data structure, which is also what your program is. Code = data. This means that (+ 2 3) is a list of three elements: +, 2 and 3, but also a program that adds 2 to 3. You can also write programs that manipulate your program. This is the ultimate metaprogramming technique! You can write programs that write programs that write programs (meta-meta-programming).

In Java you have XML configurations. Here is a Lispified configuration for a database mapper:

(table posts
  id int
  title str
  text str)


You have to define the macro "table" which transforms this code to something you want. Then Lisp runs the resulting code.

A macro is a something that takes code as its arguments. For example:
(if (= a 1) (print "a is 1") (print "a isn't 1"))
If "if" is a macro it takes three lists as arguments: the condition (= a 1), the then: (print "a is 1") and the else: (print "a isn't 1"). It returns a list (= program) which will be substituted for the (if ...). If you had cond, but not if, you could implement if in terms of cond. The if macro would return this code (= list):
(cond 
  ((= a 1) 
   (print "a is 1"))
  (else 
   (print "a isn't 1")))


Back to the table example. You can also have multiple implementations of table. One to create the table in the database for example. The table macro transforms the code into something like this:

(sql-exec "CREATE table posts ...")


But another implementation might generate domain model classes for it:

(class posts
  (attributes id title text)
  ...)


So your configuration is the program, it's no longer separate. You have the full power of Lisp in your configuration.

This means that you don't have to wait until Sun adds the "improved for" because you can now write your own.

> while objects *can* capture the lexical environment

Can they? You have to explicitely set the variables you need with obj.setX(var). I don't think that "capture" is the right word for this.

Todd Blanchard

Posts: 316
Nickname: tblanchard
Registered: May, 2003

Re: Language design Posted: Aug 25, 2006 12:24 PM
Reply to this message Reply
> > while objects *can* capture the lexical environment
>
> Can they? You have to explicitely set the variables you
> need with obj.setX(var). I don't think that "capture" is
> the right word for this.

Well, real object systems with real closures (Smalltalk) can with BlockClosure.

But the ADT/function table dispatch crowd (Java/C++) can't.

Jules Jacobs

Posts: 119
Nickname: jules2
Registered: Mar, 2006

Re: Language design Posted: Aug 25, 2006 3:55 PM
Reply to this message Reply
> Well, real object systems with real closures (Smalltalk)
> can with BlockClosure.
>
> But the ADT/function table dispatch crowd (Java/C++) can't.

OK, but that's just a wrapper for a closure?

Max Lybbert

Posts: 314
Nickname: mlybbert
Registered: Apr, 2005

Re: Language design Posted: Aug 25, 2006 4:55 PM
Reply to this message Reply
/* > while objects *can* capture the lexical environment

Can they? You have to explicitely set the variables you need with obj.setX(var). I don't think that "capture" is the right word for this.
*/

OK, rewrite to "*can* capture specific portions of the lexical environment through pass-by-reference." And (in C++) if you can craft an expression that creates a temporary to initialize your reference, the temporary is kept around as long as the reference exists ( http://publib.boulder.ibm.com/infocenter/iadthelp/v6r0/index.jsp?topic=/com.ibm.etools.iseries.langref.doc/as400clr288.htm ), so you don't have to worry about lifetime in that case.

The point was about how objects can be used to do things closures can be used for, but that doesn't mean you do them the same way.

***
And, Jacobs, thanks for the explanation about Lisp metaprogramming. I'd heard it all before, but this time the lightbulb went off (before I'd heard "the program's just data," this time it was "(+ 2 3) is an instruction *and* a list," which made it clearer).

Terje Slettebø

Posts: 205
Nickname: tslettebo
Registered: Jun, 2004

Re: language design Posted: Aug 26, 2006 12:46 AM
Reply to this message Reply
> 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.

And as you may know, such a facility is now part of the working paper for the next version of the C++ standard. :) (auto/decltype).

Morel Xavier

Posts: 73
Nickname: masklinn
Registered: Sep, 2005

Re: Language design Posted: Aug 27, 2006 3:33 PM
Reply to this message Reply
> But consider if JDBC didn't use the Iterator pattern.
> Let's say instead, the client passed in a RowHandler
> r Object and the JDBC implementation called it until it
> said 'when' and cleaned up as it needed to. Because you
> never reliquish control of the resource, you can manage it
> effectively without depending on the garbage collector.
>
> Another related example is the Iterator returned from a
> collection. If you want to wrap the collection in a
> thread-safe wrapper, you'd better read the disclaimer that
> says, "oh yeah, anyone who calls iterator needs to
> synchronize explicitly". Basically, if the client doesn't
> know or care whether the collection is thread-safe, we are
> screwed. Again, this is not a problem if you instead use
> a closure style and pass a handler to the collection.
>
These are still iterators fyi, they're examples of internal iterators as opposed to Java's or Python's external iterators.

Internal iterators are widely used in Ruby and Smalltalk, among others, but they do require support of anonymous functions and dynamic scope.

Cameron Purdy

Posts: 186
Nickname: cpurdy
Registered: Dec, 2004

Re: Verbosity in language design Posted: Aug 27, 2006 6:04 PM
Reply to this message Reply
> > I think you can use inner classes as closures,
> > *if* you make all variables one element arrays.
>
> You can modify variables for the enclosing scope in a
> closure. You can't do this in anonyous classes. There is,
> however, a hack to modify these variables: you use an
> array as a wrapper. I believe that they are equivalent to
> closures if you declare variables that way. That said, I
> don't have much Java experience, so I could be wrong.

"Java" (classes that run on the JVM) can have "closures" (anon inner classes that modify the local variables of the method that instantiated the pseudo-functor), but it would be ugly, because the admittedly not-quite-Java compiler would have to expose mutable "holders" for those variables (and the logic of the method using those variables would have to respect the holders' values) since Java does not support the concept of passing by reference.

Peace,

Cameron Purdy
http://www.tangosol.com/

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-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use