=head1 TITLE

Higher order functions

=head1 VERSION

  Maintainer: Damian Conway <[EMAIL PROTECTED]>
  Date: 4 August 2000
  Version: 1.00
  Mailing List: [EMAIL PROTECTED]
  Number: 23

=head1 ABSTRACT

This RFC proposes some syntactic sugar to simplify the creation 
of higher-order functions (a.k.a. "currying").

=head1 DESCRIPTION

One situation in which the proposed Perl C<switch> statement does not
provide a good substitute for a cascaded C<if>, is where a switch value
needs to be tested against a series of conditions. For example:

        sub beverage {
                switch (shift) {
                        case sub{ $_[0] < 10 }  { return 'milk' }
                        case sub{ $_[0] < 20 }  { return 'coke' }
                        case sub{ $_[0] < 30 }  { return 'beer' }
                        case sub{ $_[0] < 40 }  { return 'wine' }
                        case sub{ $_[0] < 50 }  { return 'malt' }
                        case sub{ $_[0] < 60 }  { return 'Moet' }
                        else                    { return 'milk' }
                }
        }

The need to specify each condition as an anonymous  subroutine is
tiresome.  Each of these small subroutines is really a "higher order"
function, which exists merely to bind the second operand of the
C<E<lt>> operator.

It is proposed that Perl reserve the bareword C<__> (underscore-underscore)
as a "placeholder" for generating higher order functions more cleanly.

That is, any expression containing C<__> anywhere that a value might
appear, will be converted to "deferred expression": a reference to a
subroutine in which the placeholders are replaced by the appropriate
number and sequence of arguments.

That is, the expression:

        $check = __ < 2 + __ * atan($pi/__) or die __;

is equivalent to:

        $check = sub (;$$$$) {
                $_[0] < 2 + $_[1] * atan($pi/$_[3]) or die $_[4]
        };

This could then be invoked:

        $check->(@args);
        
It would also be possible to interpolate arguments into a static expression
like so:

        (__ < 2 + __ * atan($pi/__) or die __)->(@args);


=head2 Examples:

With C<__>, the previous ugly case statements can be rewritten:


        sub beverage {
                switch (shift) {
                        case  __ < 10  { return 'milk' }
                        case  __ < 20  { return 'coke' }
                        case  __ < 30  { return 'beer' }
                        case  __ < 40  { return 'wine' }
                        case  __ < 50  { return 'malt' }
                        case  __ < 60  { return 'Moet' }
                        else           { return 'milk' }
                }
        }


Likewise a Tree class might provide a traversal callback like so:

        $root = Tree->new(load_from => "datafile")

        my $sum = 0;
        $root->traverse( $sum += __ );


Higher order functions would also be very useful with the proposed C<reduce> function:

        $sum  = reduce __+__ (0,@vals);
        $prod = reduce __*__ (1,@vals);


and with the new semantics of C<sort>:

        @sorted = sort(__ cmp __, @list);



=head2 Re-currying deferred expressions

The subroutines generated by a placeholder are not exactly like the
equivalent subroutines shown above. If they are called with fewer than
the required number of arguments, they return another higher order
function, which now has the specified arguments bound as well.

Thus:

        $check_or_die = $check->(@args[0..2]);

produces another deferred expression, one that requires only a single argument:

        $check_or_die->("Error message");

Arguments other than the last can also be bound by the explicit use of
placeholders:

        $check_n = $check->(__, @args[1..3]);

        # and later...

        $check_n->($n);


Thus the expression:

        $check = __ < 2 + __ * atan($pi/__) or die __;

is actually equivalent to:

        $check = sub (;$$$$) {
                  @_==0 ?  __ < 2 + __ * atan($pi/__) or die __
                : @_==1 ?  $_[0] < 2 + __ * atan($pi/__) or die __
                : @_==2 ?  $_[0] < 2 + $_[1] * atan($pi/__) or die __
                : @_==3 ?  $_[0] < 2 + $_[1] * atan($pi/$_[1]) or die __
                :          $_[0] < 2 + $_[1] * atan($pi/$_[1]) or die $_[1]
                ;
        };

Note that the level of currying for a deferred expression can always be
determined by looking at its prototype.


=head1 IMPLEMENTATION

Probably a pragma:

        use __;


=head1 SEE ALSO

RFC for switch statement

RFC for reduce function

Reply via email to