Okay, I'm not quite getting it. :)
I want to understand what it is that makes closures something that some (maybe many) consider a fundamental programming technique.
Can you offer any examples to illustrate? What purpose do they serve? Can I get by without them? I'm a Python user as well, and from what I understand Python doesn't have proper closures.
Thanks for any enlightenment on the subject.
(tye)Re: Why Closures?
by tye (Sage) on Apr 26, 2001 at 20:50 UTC
|
I assume you've read Why I like functional programming.
Functional programming is where you treat functions like
data. That is, you have functions that take functions as
arguments and return functions as values. Just using
pointers to functions (a.k.a. code references in Perl)
allows you to do that to some extent, but not enough to
really call it functional programming.
What closures add is the ability to give a subroutine
attributes (or rather, add attributes to a reference to
a subroutine). So you can write a subroutine that takes
arguments and returns a subroutine that has those arguments
as parameters. The usual example seems to be:
sub generateMultiplier {
my( $byWhat )= @_;
return sub {
my( $toBeMultiplied )= @_;
return $toBeMultiplied * $byWhat;
}
}
my @list= ( 1, 2, 3, 5, 7, 11 );
my $doubler= generateMultiplier( 2 );
my $tripler= generateMultiplier( 3 );
my @twice= map $doubler->($_), @list;
my @thrice= map $tripler->($_), @list;
which also shows how Perl isn't a functional programming
language but only allows you to use some functional
programming techniques.
-
tye
(but my friends call me "Tye") | [reply] [d/l] |
|
I was under the impression that it was quite easy to write
a function in Perl which expects some of its arguments to
be functions, and then generates a new function to return.
In fact I do exactly that quite often, and I am positive
that you know how I do it. So I suspect we are talking
past each other and mean different things.
Could you explain in more detail what you would want Perl
to do which it doesn't?
| [reply] |
|
my @twice= map $doubler->($_), @list;
my @thrice= map $tripler->($_), @list;
to be easier to write than this:
my @twice= map 2*$_, @list;
my @thrice= map 3*$_, @list;
I never said Perl doesn't let you use functional programming
techniques (in fact, I said the opposite).
Perl just has a preference for non-functional techniques.
map is one example. If you happen to have a closure,
then map requires you to write more code. If
Perl were a functional programming language, then you'd
probably have to create a closure if you had a code block
that you wanted to use with map.
-
tye
(but my friends call me "Tye") | [reply] [d/l] [select] |
|
I was under the impression that it was quite easy to write a function in Perl which expects some of its arguments to be functions, and then generates a new function to return.
Yes, you can build closures which hold other closures. Every once in a while I run into some bizarre situation where that is really, really useful.
Closures of closures can also be wickedly hard to debug.
| [reply] |
|
|
Functional programming is where you treat functions like data. That is, you have functions that take functions as arguments and return functions as values. Just using pointers to functions (a.k.a. code references in Perl) allows you to do that to some extent, but not enough to really call it functional programming.
Sorry, tye, I have to disagree with you. Functional programming is generally defined as programming without side-effects. At its most extreme, this means programming without external state of any kind.
Perl can not be considered a functional language in a purist sense, because it supports non-functional programming. However, it does support functional programming, so it is possible to "use Perl as a functional language", if one chooses.
Also, even given your definition of FP, Perl satisfies the definition perfectly. The only thing LISP-family languages can do that Perl can't, is treat function definitions as runtime-modifiable data. (That is, a lisp program can rewrite any of its functions at will.) But, while powerful, this ability is not generally considered to be essential to the definition of functional programming.
| [reply] |
|
IMHO it's better to use the positive definition "FP allows you to treat functions as data" than the negative one "FP is without side effects".
If you insisted on the negative definition then even most officialy functional languages would not be functional. And if the FP was only about the restriction it would not be very attractive ;-)
For me FP means two things. Higher order functions+closures (which is something we have in Perl) and a good general type system with real polymorphism (map: (( 'a -> 'b ), 'a list) -> 'b list) (which is something that we don't have in Perl, but since we have no types I don't feel restricted).
Jenda
P.S.: It's a shame Microsloth did not steal either from FP for it's .Net :-( C# still feels like something 30 years old :-(
| [reply] [d/l] |
|
|
|
Re: Why Closures?
by merlyn (Sage) on Apr 26, 2001 at 17:54 UTC
|
| [reply] |
|
| [reply] |
Re: Why Closures?
by frankus (Priest) on Apr 26, 2001 at 18:07 UTC
|
For me at present,
at worst a closure does the job of a static variable, at best it
can be likened to a very simple object1.
sub function_called {
my $calls = 0;
return { return ++$callS }
}
or
A larger closure to enable a generic SQL template maker and
deployer.
sub late_bound_sql {
my $string = join ('',@_); # collate args into SQL statement.
my $late_bound_fields = m/ \? /g; # Count late bound fields.
my $sql = $dbh->prepare($string); # Create sql template.
return sub {
return 0 if $#_ != $late_bound_fields; # bad param count.
for (0..$#_) { # For each argument, assume in sequence.
$sql->bind_param($_, $_[$_]); # Apply bind param.
}
if ($sql->execute()) { # Do _this_ sql.
return 1;
}
else {
return 0;
}
}
}
# Create closures.
$lb1 = late_bound("INSERT INTO foo VALUES ( x = ?, y = ?) ");
$lb2 = late_bound("INSERT INTO bar, VALUES (a = ?, b = NULL)");
# Use them.
&$lb1(2,4); # Inserts x= 2, y= 4 to table foo.
&$lb2("cuttlefish"); # Inserts a= cuttlefish, b= NULL to table bar
I make no claims as to the accuracy of this code, it's a variation,
of one I did a while ago, without nice validation or passing, but enough
I hope to clarify what I meant ;-)
--
Brother Frankus. | [reply] [d/l] [select] |
|
While closures may look like a poor man's object, I
think the reverse is closer to the case.
Using only closures you can build an OO system, with what
looks like an OO syntax. I believe that Smalltalk's OO
system works like this. Definitely Lisp's CLOS does.
By making classes multiply like rabbits, you can
mechanically translate code involving closures into
code involving objects. But I am having trouble seeing
how you could make those closures integrate into an
OO language and look like they do in a functional
language. (Perhaps my imagination is not very good
though.)
Anyways what I find that closures do which OO does not
do so well is allow you to "inline" logic that in OO
programming would involve having to create classes and
methods just to do a small thing, privately, within a
function.
For instance take the DESTROY method. Perl
offers a way to use it in an OO context. If I want to
set up some sort of finalizing action on something, I
can always create a class, with a constructor and a
DESTROY method. Each different kind of action requires
a whole class. Ick.
However if you look at my ReleaseAction, it allows
you to do the same thing, with the action defined by a
closure. Now you can, within a small function, define any
useful DESTROY behaviour that you need, without having
to create a class for it. That allows you to put the
definition of the behaviour where it is most maintainable,
right where you need it. What you are doing isn't
fundamentally different, but you think about it more
compactly.
Let me step back, and explain that differently, since in
trying to say it I think I figured out a better way to
understand it.
Conceptually an object is the concept of a thing with
multiple customized behaviours, and with a defined
structure for building a system with lots of objects,
some of which share some of the same (or similar)
behaviours.
Conceptually a closure is the concept of a thing with
a customized behaviour.
It turns out that closures are general enough to allow
you to build arbitrary objects. They may not look like
they can do that, but they can.
However if what you need is a customized behaviour,
objects have a ton of baggage that you cannot shed. This
forces you to structure your code in a less natural
manner for the ideas expressed, and that overhead limits
how you tend to think about problems, forces unnecessary
duplication of code, etc, etc. As a result there are
solutions that are quite natural using closures which
people do not naturally build without them. (And if they
tried to build those solutions without closures, the
result would be an order of magnitude less efficient, but
I digress.)
UPDATE
In reply to hding, CLOS may not need to be implemented
with closures, but it is possible to do so. Also note that I tend to be somewhat sloppy in my wording. When I start throwing around anonymous functions, I tend to call them closures even though the functions I am passed might not really be closures.
| [reply] |
|
I think your point is that a system of closures is probably
more powerful than a system of objects. I don't disagree
with that [ I won't go quite so far as to agree, but I do
suspect that you might be right (: ]. I think that
frankus' point was that a single closure is
very much like a single, very simple object. I
will go so far as to completely agree with that.
A closure is very much like an object with a single method.
This can be a great advantage if you want "an object with a
single method" because a closure doesn't require you to
create all of that OO baggage (most notably a class name
that must fit into a global namespace).
Since I'm standing up here and some of you are looking
at me, here is my breakdown of the major programming
methodologies supported by Perl:
Procedural programming has data and subroutines and you
associate them together "by hand". Object-oriented programming
has data where each type of data is tightly associated
with a collection of subroutines. Functional programming
treats subroutines as data and allows you to associate
items of data with each subroutine.
Now, if you do a whole project using one methodology, then
more differences crop up. But I like to mix methodologies
in Perl so those are the main difference for me.
So if I want a collection of subroutines that work on
similar data, I'll create a class (and make it a module).
If I have a single subroutine that works on different
instances of similar data, then I have to decide whether
I'm likely to want to add more subroutines later. If so,
I'll make a class. If not, I'll make closures.
If I have a chunk of behavior that I want to allow people to
change, then I'll probably use a code reference. If that
behavior should be associated to different instance of
similar data, then I'll use a closures.
-
tye
(but my friends call me "Tye")
| [reply] |
|
Using only closures you can build an OO system, with what looks like an OO syntax.
I believe that Smalltalk's OO system
works like this. Definitely Lisp's CLOS does.
I'm not so sure that I'd say that CLOS needs to use
closures to quite the extent you imply. Take a look for
example at the
Closette implementation
found in The Art of the Metaobject Protocol.
I think it's an exaggeration to say that this uses only
(or even primarily) closures to do its work.
Update
In reply to tilly's update :-) (Let me first qualify that
IANALI - I am not a Lisp implementor - take me with the
appropriate grain of salt.) Certainly it's possible to do
the same thing with closures. And I cheated a little bit -
the Closette implementation is a simple one that has a few
problems which AMOP suggests are probably overcome using
closures. But I'd be surprised if any widely used CLOS
implementation is largely in terms of closures - I'd expect
it to be more the melange of imperative, OO, and
functional programming that is Common Lisp (and that
is evidenced in Closette). It wouldn't
surprise me, though, if some of the Scheme object systems out there
were a little more functional, and if anyone around here knows,
it'd be interesting to know how its done in OCaml,
which is a much more functional language than CL.
| [reply] |
Re: Why Closures?
by ZZamboni (Curate) on Apr 28, 2001 at 21:09 UTC
|
As a simple example of a useful application of closures (and some
interesting discussion) see my
Setting up signal handlers for an object with access to $self
snippet. In this case, you can use closures to store the current
value of a variable ($self in this case) to provide later access
to its current value without having to explicitly store it
anywhere.
--ZZamboni
| [reply] |
|
|