Re: Functional Programming & method rewriting
by pg (Canon) on Oct 28, 2004 at 22:17 UTC
|
It becomes clear as what's going on, once we add some print outs at certain critical point (also see comments):
use Data::Dumper;
use strict;
sub prefix_later {
my ($just_once,$and_later) = @_; #passed by calling part, see below
my $ct=0;
return sub {
my (@args) = shift(); #this is just a trick ;-)
print Dumper(\@args);
print "ct = $ct\n"; #will not be reset to zero, becasue of closu
+re
((!$ct++) ? #only true for the first time
sub {
print "sub1\n";
$just_once->(@args); #hi mom
} :
sub {
print "sub2\n";
$and_later->(@args); #this was unneccessary:
$just_once->(@args); #hi mom
}
)->();
}
}
my $hi_mom = prefix_later(
sub { print "hi mom\n"; },#this is just once
sub { print "this was unneccessary:\n"; } #this is and later
);
$hi_mom->() for (1..3);
Outputs:
#first round
$VAR1 = [
undef
];
ct = 0
sub1
hi mom
#second round
$VAR1 = [
undef
];
ct = 1
sub2
this was unneccessary:
hi mom
#third round
$VAR1 = [
undef
];
ct = 2
sub2
this was unneccessary:
hi mom
| [reply] [d/l] [select] |
|
Yep, good explanation. To add some further confusion (or perhaps to explain the shift trick), if I change around the last few lines and the functions will actually take arguments ...
my $hi_mom = prefix_later(
sub { my $f = shift(); print "got: $f\n"; },
sub { print "this was unneccessary:\n"; }
);
$hi_mom->("nerp") for (1..3);
Output:
got: nerp
this was unneccessary:
got: nerp
this was unneccessary:
got: nerp
| [reply] [d/l] [select] |
Re: Functional Programming & method rewriting
by elusion (Curate) on Oct 28, 2004 at 22:56 UTC
|
You made things more complicated than they need to be. Try this:
#!/usr/bin/perl
use strict;
sub prefix_later {
my ($just_once, $and_later) = @_;
my $ct = 0;
return sub {
$and_later->(@_) if $ct++;
$just_once->(@_);
}
}
my $hi_mom = prefix_later(
sub { print "You said: ", shift, "\n"; },
sub { print "You've already said: ", shift, "!\n"; }
);
$hi_mom->("hi mom") for 1..3;
Output:
You said: hi mom
You've already said: hi mom!
You said: hi mom
You've already said: hi mom!
You said: hi mom
Why type more than you need to? And what where you doing with my (@args) = shift();?
| [reply] [d/l] [select] |
|
Yep, that is an good refactoring of prefix_later. The double closure, yeah, that's overkill, I did it just because I could. Code debate on a "I'm just screwing around" thread? :) Ok, I'll bite...
Why type more than you need to?
This philosophy is one I don't agree with, especially when developing code you are going to have to go back and tweak later.
While I hate typing way too much (java boilerplate and convention is disgusting), I do type VERY VERY fast. Many of my habits are those of pseudo-defensive programming. Even when I'm not writing "throw away" code, I don't let go of those habits easily. Why? I don't write much throw away code ... I tend to work on stuff that sticks around.
For instance (and this is just me), I *always* name my subroutine parameters and never use "shift" directly. Why? I'm being defensive because I usually want named parameters later. Every time I've done a quick shift in the middle of a block of code, I've had to go back and change it later. With @args, I might want to later replace that @args with ($gorp,$slug,@args) = shift() for instance. It leaves a place in the code to do that, and I only have to edit one line to make that change.
But I think after that double closure you can grill me all you want :) It's like climbing Mt. Everest ... "Why? Because It's There".
| [reply] [d/l] |
|
| [reply] [d/l] [select] |
|
|
|
Re: Functional Programming & method rewriting
by Joost (Canon) on Oct 28, 2004 at 22:11 UTC
|
I think it's cute :-)
And it made me think of this monster I just concocted to create an include() function for use in Text::Template templates (called from CGI::Application):
sub template {
my ($self,$filename,@args) = @_;
# create include subroutine in template package if none exists.
if (!*Bug::Reporter::In::Template::include{CODE}) {
*Bug::Reporter::In::Template::include = sub {
$self->template(shift(),@args,@_);
};
}
$self->load_tmpl($filename)->fill_in(
PACKAGE => 'Bug::Reporter::In::Template',
HASH => {
application => $self,
@args
}
);
}
# just to clarify: I'm overriding CGI::Application's load_tmpl():
sub load_tmpl {
my ($self, $file) = @_;
require Text::Template;
return Text::Template->new(
UNTAINT => 1,
TYPE => 'FILE',
SOURCE => $self->tmpl_path()."/$file",
) || die $Text::Template::ERROR;
}
Let's just hope no-one will run this across multiple threads (though Text::Template will probably break on that anyway).
update: spelling
update2: now that I think about it, it's probably a lot better to just call {$app->template('filename')} in the template.
| [reply] [d/l] |
|
Hmm, I did go the long way to do it, didn't I? :) Thanks for the idea.
sub foo {
print "hi mom\n";
my $ptr = \&foo;
*foo=sub {
print "that was unneccessary:";
$ptr->();
};
}
foo();
foo();
Hmm, more Perl voodoo. I like it.
| [reply] [d/l] |
|
Using your subroutine, try running
foo();
foo();
foo();
foo();
or for the really dangerous,
foo() for (1..1000);
| [reply] [d/l] [select] |
Re: Functional Programming & method rewriting
by diotalevi (Canon) on Oct 28, 2004 at 21:40 UTC
|
I object. There are no method calls in this meditation. | [reply] |
|
| [reply] |
Re: Functional Programming & method rewriting
by sleepingsquirrel (Hermit) on Oct 28, 2004 at 23:31 UTC
|
| [reply] |
|
| [reply] |
|
| [reply] |
Re: Functional Programming & method rewriting
by revdiablo (Prior) on Oct 28, 2004 at 23:59 UTC
|
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
If you use it more than once, it whines and complains ... I used to have it running warn() for the second function.
Oh, I see. If you use the sub more than once, you get a custom complaint, which is defined by the second coderef.
That seems a bit odd to me. I would probably make it only execute the first one once, and then have a standard warning thereafter. If it was necessary to have a custom message, I'd pass it in as a string argument, not another coderef. But perhaps your approach makes more sense for what you're doing.
Or maybe you're just doing this to have fun with closures, in which case I'm putting entirely too much thought into the whole thing. :-)
| [reply] |
A reply falls below the community's threshold of quality. You may see it by logging in.
|
Re: Functional Programming & method rewriting
by Velaki (Chaplain) on Oct 29, 2004 at 04:24 UTC
|
I like the FP style, but would a double-closure be an application of the factory design pattern; and if so, would a generalized closure generator be an implemented abstract factory?
Musing,
-v
"Perl. There is no substitute."
| [reply] |
|
would a double-closure be an application of the factory design pattern
I am sorry to pick nits, but this is something that seems to be confused a lot. I wouldn't really call this a double closure. It's just a closure that uses anonymous subroutines. The inner subroutines are not stored anywhere, so they don't have a chance to close around their lexical environment.
| [reply] |
|
Nope, they close very briefly around @args, as wrong as that is, they do. Whether they are used once or twice or thrice is indifferent to the fact that @args is lexically picked up.
| [reply] |
|
|
maybe it's a factory factory.
| [reply] |
Re: Functional Programming & method rewriting
by kappa (Chaplain) on Oct 29, 2004 at 09:58 UTC
|
You call $just_once and $and_later passing them @args which is always empty and is not used inside :) Don't get bored yet ;) | [reply] [d/l] [select] |
A reply falls below the community's threshold of quality. You may see it by logging in. |
Re: Functional Programming & method rewriting
by revdiablo (Prior) on Oct 29, 2004 at 16:29 UTC
|
The observant will note the doubly nested closure
It's me again! I'm sure you'd rather not hear any more comments from revdiablo, but I'd like to point out something I observed in another post in this thread.
This is not a double closure, it's just a single closure that uses anonymous subroutines. The inner subroutines do not close around anything, because they are not stored anywhere. They are created and destroyed each time they are used. They never get a chance to create a closure.
Update: I guess this really depends on your definition of "closure". As I said in a reply to our good friend AnonyMonk, I don't generally use the hyper-technical definition that a closure is "anything that closes around a lexical", because that's true of any subroutine that is in a lexical environment. Which, if you're using lexicals in Perl, is all of them.
I prefer to think of closures as subroutines that actually use that lexical environment for more than one invocation. I think this captures the meaning most people think of, as well as a meaning that is actually useful for something.
| [reply] |
|
{ my $foo; sub foo { 1 } } # not a closure
{ my $foo; sub foo { $foo } } # a closure
It's not about being "hyper-technical", it's about getting at the essence of what a closure is. That the bound variables doesn't go out of scope doesn't make it less of a closure, but its closureness doesn't get used. I understand that this is your point and I can almost sympathize with it, but just almost. It would be very confusing if a subroutine first wasn't a closure, but then became one when it was returned or assigned to variable with a broader scope.
Your point is supported by that we don't usually call &foo in
my $GLOBAL = 1;
sub foo {
... ;
... $GLOBAL ... ;
... ;
}
a closure. However, it is a closure and it's not wrong calling it one.
ihb
See perltoc if you don't know which perldoc to read!
Read argumentation in its context!
| [reply] [d/l] [select] |
|
First off, I want to thank you for actually engaging in a conversation, rather than simply writing me off as whiney and pedantic. I posted about something that I wanted to discuss, not just whine about. :-)
That it doesn't go out of scope doesn't make it less of a closure, but its closureness doesn't get used.
Indeed, that's true. I suppose I shouldn't try to fight against the technical definition. Rather than saying it's not a closure, I should have said it's not very useful to talk about its closure-ness.
However, it is a closure and it's not wrong calling it one.
Ok, fair enough. I officially change my position on the matter. Again, thank you for responding. I hope our friend SpanishInquisition is not too upset about the matter to read what I've written here, so he won't think I'm a completely horrible person.
| [reply] |
A reply falls below the community's threshold of quality. You may see it by logging in. |