Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Near-free function currying in Perl

by tmoertel (Chaplain)
on Nov 17, 2004 at 02:50 UTC ( #408304=perlmeditation: print w/ replies, xml ) Need Help??

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

Comment on Near-free function currying in Perl
Select or Download Code
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.

      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

        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';
Re: Near-free function currying in Perl
by BrowserUk (Pope) 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
      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.

        Do you know of a module that takes a callback for logging purposes?


        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
      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

        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.

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.
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.

      First, thanks for reading the meditation and providing feedback.

      Second, I like your idea of being able to provide a prefix and suffix. I think that I'll probably let users provide a template. Maybe:

      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!

        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.

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.

    Cheers - L~R

    Update: Added a clarification to distinguish between FP currying and the simulation of currying in Perl

      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');

      yours,
      Michael
      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.

      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.

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.

    -stvn
      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.

        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.

        -stvn
Re: Near-free function currying in Perl
by idsfa (Vicar) on Nov 17, 2004 at 17:47 UTC

    I think that this task might be better handled as a special case of Aspects. (Aspect-Oriented Programming: Couple of Short Examples) It is a classic example of cross-cutting concerns. Especially nice about this is that you can curry function calls without making any code changes.

    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
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

      No one should ever use source filters. Its a flakey practice.

        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

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://408304]
Approved by ysth
Front-paged by diotalevi
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (6)
As of 2014-09-23 03:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (210 votes), past polls