Currying (which has been discussed
before on Perl Monks) makes it easy to specialize functions by
pre-binding some of their arguments to given values. It might not
sound impressive, but after you have coded with currying for a while,
it's hard to live without it.
Unfortunately, currying in Perl is a bit awkward, typically relying
on a helper function of sorts to make the currying happen:
my $curried_foo = curry( \&foo, args... );
In this meditation, we'll do away with helper functions and
reduce the cost of currying to almost zero. Together, we'll write
AutoCurry, the tiny module that makes it happen.
(Update: If you're wondering why I didn't
use some of the existing currying modules or why I don't use
prototypes or annotations to make my implementation more like "real"
currying, please read my comment on this subject, which
explains why I think the style of currying I present below makes
more sense for Perl (5) than does signature-based currying.)
(Update: tweaked code to reduce reliance
upon INIT-time processing (thanks diotalevi!))
Our goal is to make currying free, like it is in some other
programming languages. (In Haskell, for example, you don't need to say
that you want to curry a function; you just call it with fewer than
the expected number of arguments, and the currying happens
automatically.)
Why do this in Perl?
Before getting to AutoCurry, let's spend a few moments
justifying the exercise. Why should we try to reduce the cost of
currying in Perl? The answer is because currying reduces the cost of
reusing functions, making reuse practical in more situations, and that
in turn reduces the cost of programming in general. In short, with
currying we reinvent the wheel less. If we can reduce the cost of
currying further, we might be able to achieve further cost reductions.
As a motivating example, consider the case of logging. Let's
say that we have the following generalized logging function:
sub log_to_handle {
my ($fh, $heading, $message) = @_;
print $fh "$heading: $message", $/;
}
It's a simple function for the sake of our example, but let's imagine
that it's complex and would be costly to rewrite.
Let's further say that later we're working with a server framework
that lets us configure it with a logging function to use when the
server emits diagnostic messages:
my $app_server = AppServer->new( logger => $mylogger, ... );
The application server expects $mylogger to be a logging
function that takes a single argument, the message to be logged.
It would be nice to be able to reuse our existing, 3-argument
logging function for this purpose. We can do this by adapting it to
the application server's expectations. Because the application
server expects a 1-argument function and we have a 3-argument
function, we must specialize away the extra arguments. We'll
do this by binding $fh to STDERR and
$heading to "app server".
In some programming languages, we would need to write a wrapper
function to specialize the function:
sub app_server_diagnostic_logger {
log_to_handle( *STDERR, "app server", @_ );
}
$app_server = AppServer->new(
logger => \&app_server_diagnostic_logger, ...
);
But Perl gives us a less-expensive way. We can use an anonymous
subroutine to create an on-the-fly wrapper, tailored to our needs:
$app_server = AppServer->new(
logger => sub {
log_to_handle( *STDERR, "app-server", @_ )
}, ...
);
Still, we can do better. We can use a currying helper function to
take the cost down another notch and also to make clear our intent
to specialize an existing function:
$app_server = AppServer->new(
logger => curry( \&log_to_handle, *STDERR, "app-server" ),
...
);
That's pretty good, but specialization in Perl is still more expensive
than in some other languages. It would be great to reduce the cost
to the bare minimum, where a regular function call is automatically
curried if it doesn't receive all of the arguments it wants:
$app_server = AppServer->new(
logger => log_to_handle( *STDERR, "app-server" ),
...
);
That's the ultimate goal: zero-cost currying.
The idea behind AutoCurry
AutoCurry gets us very close to the goal. We can't quite make it all
the way because functions in Perl can accept varying numbers of
arguments, and thus it's hard for us to determine reliably when
currying is implied by analyzing function calls. For this reason, we
take the practical road and rely upon a hint from the programmer to
tell us when currying is expected. (Seen from this light, calling the
curry helper function could be considered a rather expensive
hint. We want to make the hint less expensive.)
The hint that we will use is to append the suffix "_c" to
any function call that we want to have currying semantics. To show
how it works with our running example:
$app_server = AppServer->new(
logger => log_to_handle_c( *STDERR, "app-server" ),
...
);
That's only two characters away from the ideal, which
is probably as close as we can practically make it.
Underneath, the implementation relies upon double-currying and some
symbol-table manipulation to create curried variants of our normal
functions.
Let's walk through the strategy. First, we need a run-of-the-mill
currying helper:
sub curry {
my $f = shift;
my $args = \@_;
sub { $f->(@$args, @_) };
}
Then, to create a currying variant of a normal function, we "double
curry" it and store the resulting function in the symbol table under
the appropriate _c name:
*log_to_handle_c = curry( \&curry, \&log_to_handle );
In essence, each _c function is a partially applied call to
curry that specializes the corresponding normal, non-curried
function by calling curry again.
Now, to make the approach cost effective, all we need to do
is automate it and bring it to a larger scale.
Mass production
For maximum convenience, we would like to curry-enable every function
in our namespace automatically. The first step, then, is to scan our
namespace for functions. We can do this by scanning its symbol table
and extracting the names associated with non-empty CODE
slots:
sub get_function_names_from_package {
no strict 'refs';
my $pkg = shift || caller;
my $symtab = *{ $pkg . "::" }{HASH};
grep *$_{CODE}, # drop symbols w/o code
map $pkg."::$_", # fully qualify
grep !/^_|^[_A-Z]$/, # drop _underscored & ALL_CAPS
keys %$symtab; # get all symbols for package
}
(Note that we skip functions whose names start with an underscore or
are ALL CAPS. Such functions are often system routines that we don't
have reason to curry.)
To see how the function works, let's try it on a small package:
{ package Test;
sub one { }
sub two { }
sub three { }
$Test::not_a_function = 1;
}
my @names = get_function_names_from_package("Test");
print "@names", $/;
# Test::three Test::one Test::two
Now, all that's left is to iterate over the names and create
corresponding _c versions that implement our double-curried
strategy:
for (@names) {
no strict 'refs';
my $curried_name = $_ . "_c";
*$curried_name = curry( \&curry, \&$_ );
}
And that's the essence of AutoCurry.
To wrap it up, we'll place everything in the AutoCurry package,
along with some documentation and a few extra helper functions. As a
further convenience, the module will accept instructions about what to
auto-curry via its import list:
use AutoCurry qw( foo ); # pass ':all' to curry all functions
sub foo { print "@_$/"; }
# currying variant, foo_c, is created automatically
Implementing the import function and robustifying the code
above is straightforward, and so I'll stop the meditation here. (If
you're curious, I have included the complete code for the module
below. It contains fewer than sixty lines of code.)
Thank you!
Thanks for taking the time to read this meditation.
If you have any criticisms or comments, please let me know.
Also, if you can help me improve my writing, I would greatly
appreciate your suggestions.
Cheers,
Tom
The code for AutoCurry.pm
package AutoCurry;
# Tom Moertel <tom@moertel.com>
# 2004-11-16
# $Id: AutoCurry.pm,v 1.3 2004/11/17 04:56:17 thor Exp $
=head1 NAME
AutoCurry - automatically create currying variants of functions
=head1 SYNOPSIS
use AutoCurry qw( foo ); # pass :all to curry all functions
sub foo { print "@_$/"; }
# currying variant, foo_c, is created automatically
my $hello = foo_c("Hello,");
$hello->("world!"); # Hello, world!
$hello->("Pittsburgh!"); # Hello, Pittsburgh!
=cut
use Carp;
my $PKG = __PACKAGE__;
sub curry {
my $f = shift;
my $args = \@_;
sub { $f->(@$args, @_) };
}
sub curry_package {
my $pkg = shift || caller;
curry_named_functions_from_package( $pkg,
get_function_names_from_package( $pkg )
);
}
sub curry_named_functions {
curry_named_functions_from_package( scalar caller(), @_ );
}
sub curry_named_functions_from_package {
no strict 'refs';
my $pkg = shift() . "::";
map {
my $curried_name = $_ . "_c";
carp "$PKG: currying $_ over existing $curried_name"
if *$curried_name{CODE};
*$curried_name = curry( \&curry, \&$_ );
$curried_name;
} map { /::/ ? $_ : "$pkg$_" } @_;
}
sub get_function_names_from_package {
no strict 'refs';
my $pkg = shift || caller;
my $symtab = *{ $pkg . "::" }{HASH};
grep *$_{CODE}, # drop symbols w/o code
map $pkg."::$_", # fully qualify
grep !/^_|^[_A-Z]+$/, # drop _underscored & ALL_CAPS
keys %$symtab; # get all symbols for package
}
my @init;
sub import {
shift; # don't need self
my $caller = caller;
push @init, curry_package_c($caller) if grep /^:all$/, @_;
curry_named_functions_from_package($caller, grep !/^:/, @_);
}
INIT { finish_initialization() }
sub finish_initialization {
$_->() for @init; @init = ();
}
# physician, curry thyself!
curry_named_functions(qw(
curry_package
));
1;
__END__
=head1 DESCRIPTION
This module automatically creates currying variants of functions. For
each function C<foo>, a currying variant C<foo_c> will be created that
(1) captures whatever arguments are passed to it and (2) returns a new
function. The new function awaits any new arguments that are passed
to I<it>, and then calls the original C<foo>, giving it both the
captured and new arguments.
If C<foo> is a function and C<foo_c> is its currying variant, then the
following are equivalent for all argument lists C<@a> and C<@b>:
foo(@a, @b);
foo_c(@a, @b)->();
foo_c()->(@a, @b);
foo_c(@a)->(@b);
do { my $foo1 = foo_c(@a); $foo1->(@b) };
=head2 use AutoCurry I<names>
You can create currying variants at C<use> time by listing the
functions to be curried:
use AutoCurry qw( foo bar );
Or, if you want to curry everything:
use AutoCurry ':all';
=head2 curry_named_functions(I<names>)
You can also create variants at run time:
my @created_variants =
AutoCurry::curry_named_functions(qw( foo bar ));
The fully-qualified names of the created functions are returned:
print "@created_variants";
# main::foo_c main::bar_c
If you're writing a module, this list of names is handy for augmenting
your export lists.
=head1 MOTIVATION
Currying reduces the cost of reusing functions by allowing you to
"specialize" them by pre-binding values to a subset of their
arguments. Using it, you can convert any function of I<N> arguments
into a family of I<N> related, specialized functions.
Currying in Perl is somewhat awkward. My motivation for
writing this module was to minimize that awkwardness and
approximate the "free" currying that modern functional
programming languages such as Haskell offer.
As an example, let's say we have a general-purpose logging function:
sub log_to_file {
my ($fh, $heading, $message) = @_;
print $fh "$heading: $message", $/;
}
log_to_file( *STDERR, "warning", "hull breach imminent!" );
If we're logging a bunch of warnings to STDERR, we can save some work
by specializing the function for that purpose:
my $log_warning = sub {
log_to_file( *STDERR, "warning", @_ );
};
$log_warning->("cap'n, she's breakin' up!");
The C<log_warning> function, being tailored for the purpose, is easier
to use. However, having to create the function is a pain. We're
effectively currying by hand. For this reason, many people use a
helper function to curry for them:
$log_warning = curry( \&log_to_file, *STDERR, "warning" );
An improvement, but still far from free.
This module does away with the manual labor altogether by creating
currying variants of your functions automatically. These variants
have names ending in a C<_c> suffix and I<automatically curry> the
original functions for the arguments you give them:
use AutoCurry ':all';
$log_warning = log_to_file_c( *STDERR, "warning" );
$log_warning->("she's gonna blow!");
The total cost of currying is reduced to appending a C<_c> suffix,
which is probably as low as it's going to get on this side of Perl 6.
=head1 AUTHOR
Tom Moertel <tom@moertel.com>
$Id: AutoCurry.pm,v 1.3 2004/11/17 04:56:17 thor Exp $
=head1 COPYRIGHT and LICENSE
Copyright (c) 2004 by Thomas G Moertel. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=cut
Re: Near-free function currying in Perl
by diotalevi (Canon) on Nov 17, 2004 at 04:03 UTC
|
I assume you'll be uploading this to CPAN. Where's the test suite? Could you accomodate using some other namespace other than '_c'? I use that when referring to a column attribute when using Alzabo. I'd opt to be verbose and just use the '_curry' namespace instead.
# the first argument to import may be some configuration
use AutoCurry ( { namespace => '_curry' }, LIST );
Also note that your INIT block won't run when your module is loaded during runtime and from common mod_perl configurations. You'll need to expose that block in a named subroutine to give those other people a chance to run that code. You could also just run that from your import() function so your users won't need to care about that detail. | [reply] [d/l] |
|
Thanks for taking the time to read the meditation and respond.
Your views are always welcomed.
Yes, I'll put this up on CPAN once I flesh it out. This meditation
is my first opportunity for feedback, and so I expect a round of revision
(including a test suite) before I upload.
Could you accomodate using some other namespace other
than '_c'?
Yes.
That said, I'm reluctant to increase the length of the currying
suffix because it increases the usage cost beyond the point where it
seems like a mere notational convention. Nevertheless, I can see the
opportunity for conflict with existing naming schemes, and tastes do
vary, and so I ought to make the suffix configurable.
Also note that your INIT block won't run when your module is loaded during runtime and from common mod_perl configurations. You'll need to expose that block in a named subroutine to give those other people a chance to run that code.
It is exposed already via curry_named_functions and
curry_package, although the latter isn't documented yet.
You could also just run that from your import() function so your users won't need to care about that detail.
That would prevent the most common usage from working:
use AutoCurry ':all';
sub one { }
sub two { }
The import function would be called before one and
two were even parsed, and so it couldn't possibly load them
from the symbol table. That's why the import function
must queue up operations to until INIT time.
Thanks again for your response.
Cheers, Tom
| [reply] [d/l] |
|
Finally, AutoCurry has hit CPAN. Sorry it took so long.
Cheers, Tom
| [reply] |
|
You have still neglected to leave the user with the ability to use a different suffix. This suffix is already used by Alzabo::MethodMaker (for row columns) though it is configurable. If I intended to use both Alzabo and AutoCurry, I'd have to do some fancy footwork with the symbol table to rename all the curried functions before other _c functions were installed (or is it the other way, I forget). Your accomodation may be as simple as providing an overridable package scalar.
BEGIN { $AutoCurry::Suffix = '_not_c'; }
use AutoCurry ':all';
| [reply] [d/l] |
|
|
|
Re: Near-free function currying in Perl
by BrowserUk (Patriarch) on Nov 17, 2004 at 05:42 UTC
|
Sorry Tom, but I'm still not getting it!
Your premise starts by saying that this:
$app_server = AppServer->new(
logger => curry( \&log_to_handle, *STDERR, "app-server" ),
...
);
is ...take[ing] the cost down another notch and also to make[ing] clear our intent", when compared with:
$app_server = AppServer->new(
logger => sub { log_to_handle( *STDERR, "app-server", @_ ) },
...
);
But I simply do not see that is true.
Apart from that calling a function log_to_handle() is documenting how the function works, not what the function does; I would either reject, or modify any application server module that required me to give it function for it to call every time it wanted to log a message.
It is just so much easier and more efficient to allow me to configure the heading and destination filehandle as individual parameters at instantiation.
All your curry() is doing, is complicating a very simple piece of code, in order to justify the introduction of the need for autocurrying.
But there is another, more serious problem with the idea of autocurrying.
If AppServer is a class, as implied by the ->new(...) instantiation, then it would be normal practice for me to be able to instantiate two instances. If I pass in a coderef to each instance, then each instance can store those as instance data and will call the appropriate logger function automatically.
But if instantiating an instance of AppServer class is going to blythly export curried functions into the caller's namespace, then there is going to be a conflict between those exported for use with the first instance, and those for the second etc.
I also wonder how this is going to work with OO code where the first parameter is $self?
Namespace pollution will abound, to create lots of autocurrying functions that in all honesty, I cannot remember using a module that might lend itself to their use.
Your example aside, which seems somewhat contrived anyway, I cannot think of a single instance of ever needing or wanting to constantise one or more parameters that I had to pass on many calls to a function.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
"Time is a poor substitute for thought"--theorbtwo
"Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
| [reply] [d/l] [select] |
|
Thanks for your feedback! (I especially appreciate feedback
from you because you take the time to explain your concerns,
and you don't dismiss things offhand. When you say that you
don't see the value of something, I know that it's not
because you aren't trying. Thanks!)
Let me see if I an address your concerns.
But I simply do not see that (currying is less
expensive than creating an anonymous wrapper
function).
Cost is in the eye of the be-payer, and so I won't tell you
that you're wrong. However, I would like to suggest that
your cost model would be different if you were familiar
with currying.
Later on, you write this:
I cannot think of a single instance of ever needing or
wanting to constantise one or more parameters that I had to pass on
many calls to a function.
This suggests that currying is indeed a foreign concept to you. When
you see currying, then, the cost naturally goes up because it doesn't
seem as nearly straightforward as "simply" writing anonymous-subroutine
wrapper.
But let me argue that currying is the easier of the two to
understand (once currying is familiar). Currying does only one thing:
binds arguments to values. Anonymous wrapper subs, on the other hand,
can do anything. So, using the code below as an example, you
must look closely at the anonymous-subroutine version to ensure that
all it's doing is binding the first two arguments:
sub { log_to_handle( *STDERR, "app-server", @_ ) }
curry( \&log_to_handle, *STDERR, "app-server" )
log_to_handle_c( *STDERR, "app-server" );
The second version, by definition, can only bind the first
two arguments of log_to_handle. There is no other
possibility. Please consider that this constraint reduces the cost of
understanding the second w.r.t. the first. (Again, assuming that you
have already paid the price to understand currying.) And the third
version is even simpler (once it is understood that the _c
suffix is notation that implies currying.) It is identical in meaning
to the second but shorter and closer to the true currying that some
other programming languages offer.
All your curry() is doing, is
complicating a very simple piece of code, in order to justify the
introduction of the need for autocurrying.
No doubt about it, my example sucks as a motivating example for
currying. I doubt that anybody who doesn't already value currying is
going to bother reading beyond the contrived back-story. (Lesson for me: Invest more time in selecting examples that are simple, illustrative, and yet realistic.)
I would either reject, or modify any application
server module that required me to give it function for it to call
every time it wanted to log a message.
Please don't read too much into the back-story. Don't focus on the
obviously contrived "ooh, we have an application server that we must
provide with a logging callback." Instead, see it as "how do we
glue the function we want of use into the hole we want to fill, when
their shapes are different?"
But there is another, more serious problem with the
idea of autocurrying.
(I)f instantiating an instance of AppServer class is going to
blythly export curried functions into the caller's namespace,
...
It isn't. Here's the situation:
#!/usr/bin/perl
use warnings;
use strict;
use AppServer;
use AutoCurry ':all';
sub log_to_handle { ... }
my $appserver = AppServer->new(
logger => log_to_handle_c( *STDERR, "app server" )
);
Only log_to_handle_c will be created, and it will be stored
in the same namespace as the original log_to_handle
(main::). Further, it will happen only once, after the
application code is compiled but before it starts executing.
Thanks again for providing a wealth of feedback. I hope
that I was able to address some of your concerns. Please
don't hesitate to follow up, and I will do my best to respond.
Cheers, Tom
| [reply] [d/l] [select] |
|
A perl noob (but veteran news writer) re your self-deprecated "motivating example":
On the one hand (++), you give the reader a warning, "It's a simple function for the sake of our example, but let's imagine that it's complex and would be costly to rewrite." Good; 'cuz it leads to the inference that your basics will be more easily absorbed using a "simple" example.
To head off critiques such as some already offered here, though, you might want to offer a much more complex function as an example of why currying is desireable -- perhaps even before the existing one.
For example, you may have a specialized function like this:
((sorry way too new to write that)).
Now, suppose new management wants you to write a package that does essentially the same thing, but with only a few possible inputs.
But rather than do it with a complex example (that will make it harder to focus on currying), let's pretent the original function is:....
Re the writing: splendidly clear and concise. This noob had difficulty only with one phrase... and that was simple lack of background... most of which you subsequently supplied. Well done!
Will be watching the tread.
| [reply] |
|
I think you've got things reversed. log_to_handle() is not part of the framework, it's a function that Tom already has lying around. The framework does it's logging by running a coderef you provide and passing in the log message as the signle argument.
# deep inside the framework
&$logger("frobnication successful");
Tom's problem is that his log_to_handle() function is expecting several arguments including a filehandle and so he can't reuse in the framework. He would need it to be
# deep inside the framework
&$logger($LOGHANDLE, "frobnication successful");
Currying allows Tom to take log_to_handle and turn it into a function that already knows what handle to use and only needs a single argument. One way to do that is
my $logger = sub {
my $msg = shift;
log_to_handle($MYHANDLE, $msg);
};
and then pass $logger into the framework. Of course that's a lot more typing than we'd like so with AutoCurry you can just do
my $logger = log_to_handle_c($MYHANDLE);
the _c at the end means that rather than running the log_to_handle function we want a new coderef which will be suitable for the framework.
| [reply] [d/l] [select] |
|
| [reply] |
|
|
|
Re: Near-free function currying in Perl
by dragonchild (Archbishop) on Nov 17, 2004 at 13:15 UTC
|
I'd like to second the comment about allowing a user-defined suffix. But, I'd like to see the ability to use a prefix instead of/in addition to a suffix. For example, I work with some people whose Perl skills are ... well ... lacking. Instead of forcing them to look at the end, I may prefer to have them look at the beginning. So, instead of log_handle_c(), I may like to have CURRY_log_handle(), or somesuch.
So, I would say allow for both a suffix and a prefix, if desired. If nothing is specified, default to suffix=>'_c'. If a suffix is specified, use it instead. If a prefix is specified, but no suffix, just use the prefix. If both are specified, use both. Someone may want to see CURRIED_log_handle_CURRIED() as their name, for readability.
Remember - you're providing low cost as the default. But, maybe I want to pay a higher typing cost in order to achieve a lower overall maintenance cost. Your module should allow that, otherwise its adoption rate may not be as high as you would like.
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
| [reply] |
|
template => "${_}_c"
Nevertheless, in the docs I will try to discourage using anything but the default. While you are no doubt correct that using a more explicit prefix/suffix may reduce the cost of maintenance locally for some settings, not having a common usage across the community may increase cost globally.
For what it's worth, in programming languages with support for real currying, there is no notation whatsoever for currying – curried calls and regular calls are look the same (because underneath they are the same) – and it's not confusing once you get the hang of it. (In fact, it's liberating.)
Thanks, again!
| [reply] [d/l] |
|
Just out of curiousity, is it because standard Perl functions do not specify how many parameters they need at a minimum, so the compiler cannot tell if a function is satisfied? Because, if it is, couldn't we just use prototypes? For example:
sub foo ($$;$) { ... }
my $curried_foo = foo( 1 );
my $return_value = foo( 1, 2 );
I'm thinking a source filter would be needed to convert
sub foo ($$;$) { ... }
to
sub foo
{
if (@_ < 2) {
my $arg = shift;
return sub { foo($arg, @_) };
}
...
}
Wouldn't that work?
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
Re: Near-free function currying in Perl
by Limbic~Region (Chancellor) on Nov 17, 2004 at 14:30 UTC
|
tmoertel,
Ok - I have to admit that I don't fully understand currying or a lot of other concepts from functional programming (FP). I have a few questions - some in general and some specific to your implementation.
A common way in Perl to simulate currying is as follows:
The next question is with your statement:
We can't quite make it all the way because functions in Perl can accept varying numbers of arguments, and thus it's hard for us to determine reliably when currying is implied by analyzing function calls.
Prototypes, as evil as they are, do allow for optional arguments or you could always count @_. I don't see why this is a problem.
My final question for now is an apparent flaw in the design. Currying, as I understand it, only works if I don't have the last x arguments. What if I have the middle argument and the entire function requires 5? In Perl, that would be easy because I would use a hash ref and "name" my arguments. I don't see how currying helps in this situation (real currying, not Perl's substitute).
Thanks by the way - I am impressed.
Update: Added a clarification to distinguish between FP currying and the simulation of currying in Perl
| [reply] [d/l] [select] |
|
L~R, thanks
for dropping by! You have some great questions, ones that I think
a lot of people share. I'll do my best to answer them.
greeting('Woah'); # Waits
...
greeting('Nelly'); # prints now that it has both arguments
How does it know that I am finishing off one and not
starting another? Or if the 3rd time I called greeting() I did it with
two arguments - what then?
Actually, currying doesn't work that way. Each of the calls above
would result in a partial application of the greeting
function and result in a new function that, if given the final
argument, would emit the final result. Currying doesn't rely upon a
stateful function that accumulates arguments until it is satisfied.
Each call is independent. I explain more on this later.
Regarding how the language knows when the final argument is
supplied, "real" currying is typically paired with a type system that
can infer when all arguments have been satisfied. To use Haskell as
an example, the greeting function would be written like so:
greeting verb object = putStrLn (verb ++ ", " ++ object)
From this, Haskell can infer the following type:
greeting :: String -> String -> IO ()
It says, "greeting takes a string and returns a function that takes
another string and returns an IO action." (The notation
a->b means "function from type a to type
b" and is right-associative.) This is more obvious
from the fully parenthesized type:
greeting :: String -> ( String -> IO () )
Thus the following are all the same:
greeting "Hello" "World"
(greeting "Hello") "World"
((greeting) "Hello") "World"
Let's take a look at the types of the intermediary expressions:
greeting "Hello" "World" :: IO ()
greeting "Hello" :: String -> IO ()
greeting :: String -> String -> IO ()
So, to answer your first question, Haskell knows what to do
at each step because it keeps track of the types. If you
supply both arguments, the result is an IO action –
"Hello, World" is printed. If you supply only the first
argument, the result is a function that takes a string
and returns an IO action. And if you supply zero arguments,
the result is a function that takes a string and returns
a function than takes a string and returns an IO action.
(The reality is a bit more complex. The short of it
is that the intermediate functions are optimized away
when they're not needed.)
The next question is with your statement:
We can't quite make it all the way because
functions in Perl can accept varying numbers of arguments, and thus
it's hard for us to determine reliably when currying is implied by
analyzing function calls.
Prototypes, as evil as they are, do allow for optional arguments
or you could always count @_. I don't see why this is a
problem.
It's not a problem per se, but rather a design decision.
Perl lets us write functions with variable numbers of arguments, and
it offers wonderful argument-splicing call semantics. Why create a
Perl "currying" that doesn't let us take advantage of these strengths?
I want to be able to curry functions like the following, where there
is no ahead-of-time notion of how the arguments will be used:
sub foo { print "@_" }
What this means, however, is that we're not really currying.
What we're doing is a single-shot argument binding. But it's close
enough to true currying for most practical intents and purposes.
My final question for now is an apparent flaw in the
design. Currying, as I understand it, only works if I don't have the
last x arguments. What if I have the middle argument and the entire
function requires 5?
In most languages with native currying, higher-order functions are
also available. They let you manipulate other functions to reorder
arguments (among other things). Nevertheless, in a world with
pervasive currying, most people design their functions so that the
arguments most likely to be specialized come first. Typically, what
you want to specialize on will be one of the first two arguments,
and getting to them is idiomatic.
In Haskell, for example, the higher-order function flip
flips the order of first two arguments of its target function. This
lets you specialize the second argument and leave the first alone:
(-) 2 1 ==> 1
flip (-) 2 1 ==> -1
dec1 = flip (-) 1
dec1 2 ==> 1
(Note: Most experienced Haskell programmers would use the operator section
(- 1) instead of creating dec1.)
Thanks again for your comments. I hope I was able to answer
your questions. Please let me know if any of my explanations
aren't clear.
Cheers, Tom
P.S. Writing a library of zip and
fold functions for Tool::Box is what motivated me to
create AutoCurry! These functions really shine when low-cost
currying is available.
| [reply] [d/l] [select] |
|
Howdy!
greeting('Hello', 'Good Looking'); # prints immediately
greeting('Woah'); # Waits
...
greeting('Nelly'); # prints now that it has both arguments
I don't think so. greeting('Woah') would, as far as I can tell, yield a
new function that takes a single argument, so it would have to be used in
a context where that other argument will appear.
I'd expect it to be analagous to greeting('Whoa')->('Nelly');
| [reply] |
|
If I understand correctly, you can still curry with named parameters. You just have to add a little more intelligence. Basically, currying means that the function call was incomplete - we need more parameters before we are satisfied. It's easy to do that with positional - did I get enough stuff. But, using something like Params::Validate, you could build currying for named parameters.
Of course, the trick is making sure you have a good API for the developer to specify currying. I suspect the best way is to just define the API, then have the function automatically curry whenever a required argument is missing.
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
| [reply] |
Re: Near-free function currying in Perl
by stvn (Monsignor) on Nov 17, 2004 at 16:07 UTC
|
Very nice meditation, Tom. Being a fan of functional programming myself, I really enjoy reading your meditations. However, being a user of OOP programming too, I despise namespace pollution. I like your idea, but I would like to see it be done without the need for creating new functions in the namespaces. This alone would make me not use this module desipte the fact I can see a number places something like this would be handy.
I suggested above (in response to dragonchild's thought on prototypes) the possibility of using attributes to defined the curry-ability of a function. I think this would be a nicer way to do things (assuming it is possible of course), since it would provide the transparent currying you are after (ala Haskell, Std ML, etc). For me, I would rather have the transparency, in fact I think I would even choose curry(\&func ...) over adding the '_c' to a function as well. Naming conventions are a very sensitive issue for many people and I would think even with having all the suffix and prefix options would still not be enough for some, me in particular ;-)
Again, excellent meditation, I really enjoy these.
| [reply] [d/l] |
|
package AutoCurry;
require v5.6.1;
use Attribute::Handlers::Prospective;
sub UNIVERSAL::curry : ATTR(CODE,RAWDATA) {
my ($package, $symbol, $referent, $attr, $data, $phase) = @_;
my $num = () = $data =~ /(\$)/g;
*{"$package".'::'.*{$symbol}{NAME}} = sub {
if (@_ < $num) {
my @x = @_;
return sub { $referent->(@x, @_ ) };
}
$referent->(@_);
};
}
1;
__END__
#############
demo.pl
#############
#!perl
use strict;
use warnings;
use AutoCurry;
sub foo : curry($$$) {
print "Hello @_\n";
};
foo( 'a', 'b', 'c' );
my $ref = foo('d','e');
$ref->('f');
$ref->('g');
Obviously, improvements can be made, but I think this does what is desired. There is a slight performance penalty, but it's not bad at all.
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
| [reply] [d/l] |
|
LOL, you beat me by only a few minutes, I was just going to posts this.
Although to be honest, neither of our implementations, nor Tom's do what Haskell and Standard ML do, which is too keep currying until all the functions arguments are satisfied.
my $curried_foo = foo(1);
my $even_more_curried_foo = $curried_foo->(2);
print $even_more_curried_foo->(3); # now we execute the function
And from my (limited) understanding of prototypes, it seems that this may not be possible since seems it is difficult to assign an attribute to a closure.
| [reply] [d/l] [select] |
|
|
Re: Near-free function currying in Perl
by Jenda (Abbot) on Nov 17, 2004 at 20:55 UTC
|
I don't like the syntax. When looking at the code I would definitely not expect foo_c(1,2) to be "equivalent" to sub {foo(1,2,@_}. I think the currying would have to have a very very nice syntax to be used in place of anonymous subs. A syntax that would look readable enough to me is
my $fun = foo( 1, 2, ...);
# or using your example
$app_server = AppServer->new(
logger => log_to_handle( *STDERR, "app-server", ...),
other_option => 5,
);
(Except that it would colide with the common usage of three dots in pseudo code.)
It's actually doable, but AFAIK only via source filters. And it is not that long actually:
package Curry::Dots;
use Filter::Simple;
my $braces = qr{
\(
(?:
(?> [^\(\)]+ ) # Non-parens without backtracking
|
(??{ $braces }) # Group with matching parens
)*
\)
}x;
sub curry {
my $f = shift;
my $args = \@_;
sub { $f->(@$args, @_) };
}
FILTER_ONLY
code => sub {
return unless /,\s*\.\.\.\s*\)/;
s/\&\$(\b\w+)\s*\(([^\(\)]*(?:$braces[^\(\)]*)*)\s*,\s*\.\.\.\
+s*\)/Curry::Dots::curry (\$$1, $2)/g;
s/(\$\b\w+)\s*->(\w+)\s*\(([^\(\)]*(?:$braces[^\(\)]*)*)\s*,\s
+*\.\.\.\s*\)/Curry::Dots::curry ($1->can('$2'), $1, $3)/g;
s/(\b\w+)\s*->(\w+)\s*\(([^\(\)]*(?:$braces[^\(\)]*)*)\s*,\s*\
+.\.\.\s*\)/Curry::Dots::curry ($1->can('$2'), '$1', $3)/g;
s/\$(\b\w+)\s*->\s*\(([^\(\)]*(?:$braces[^\(\)]*)*)\s*,\s*\.\.
+\.\s*\)/Curry::Dots::curry (\$$1, $2)/g;
s/(\b\w+)\s*\(([^\(\)]*(?:$braces[^\(\)]*)*)\s*,\s*\.\.\.\s*\)
+/Curry::Dots::curry (\\\&$1, $2)/g;
};
1;
and the examples:
use strict;
use Curry::Dots;
sub foo {
print "@_\n";
}
sub Obj::method {print "Obj::method( @_)\n"}
sub Obj::new {bless {}, 'Obj'}
my $f1 = foo(1, 2, ...);
$f1->(3);
my $f2 = &$f1( 99, ...);
$f2->(0);
my $f3 = $f1->(7,... );
$f3->(123);
my $obj = new Obj;
my $f4 = $obj->method(987, 654, ...);
$f4->(321);
my $f5 = Obj->method(55,...);
$f5->(22);
Tested with Perl v5.8.0 (ActivePerl build 805).
It only supports the simpler types of calls like foo(params, ...), &$foo(params, ...), $foo->(params, ...), $obj->Method(params, ...) and Class->Method(params, ...) and it uses just \w+ for variable/function/method/class names which definitely is not correct regexp for identifiers in Perl.
Jenda
We'd like to help you learn to help yourself
Look around you, all you see are sympathetic eyes
Stroll around the grounds until you feel at home
-- P. Simon in Mrs. Robinson |
| [reply] [d/l] [select] |
|
No one should ever use source filters. Its a flakey practice.
| [reply] |
|
That's why I said it's AFAIK doable ONLY using source filters. It was a nice exercise writing the module, I do like the syntax that it allows, but I don't think I will ever use it in production code.
The code could be improved to match the Perl identifiers and variables better, but I'm not sure it'd be worth it. If anyone wants to extend the code, use it and even release it to CPAN it's fine with me. I doubt I ever will. Though ... you never know ;-)
Update: BTW, I searched all my Perl sources including the perl instalation and only found /,\s*\.\.\.\s*\)/ in PODs and comments and in one string printed by DBI.pm, one in Tk\X11Font.pm, one in Benchmark.pm and one in DB_File.pm.
Jenda
We'd like to help you learn to help yourself
Look around you, all you see are sympathetic eyes
Stroll around the grounds until you feel at home
-- P. Simon in Mrs. Robinson |
| [reply] [d/l] |
Re: Near-free function currying in Perl
by fergal (Chaplain) on Nov 17, 2004 at 12:24 UTC
|
Cool idea. How about an alternative interface for lazy currying so that I could do
use AutoCurry qw( AUTOLOAD );
and get an AUTOLOAD subroutine that will dynamically implement function_c.
| [reply] [d/l] |
Re: Near-free function currying in Perl
by idsfa (Vicar) on Nov 17, 2004 at 17:47 UTC
|
use Data::Dumper;
use Aspect;
before {
my $context = shift;
if (@{$context->params} < 1)
{
$context->append_param($context);
}
} call 'Data::Dumper';
print Dumper();
Of course, Aspect is a lot more overhead, so you need to choose the implementation that works better for you (but if you have a million function calls to curry ...)
The intelligent reader will judge for himself. Without examining the facts fully and fairly, there is no way of knowing whether vox populi is really vox dei, or merely vox asinorum. -- Cyrus H. Gordon
| [reply] [d/l] |
|
|