blog | Perlgeek.de :: Currying (original) (raw)
NAME
"Perl 5 to 6" Lesson 28 - Currying
SYNOPSIS
use v6;
my &f := &substr.assuming('Hello, World'); say f(0, 2); # He say f(3, 2); # lo say f(7); # World
say .map: * x 2; # aabbcc say .map: *.uc; # ABC for ^10 { print .[$_ % *]; # RGBRGBRGBR }
DESCRIPTION
Currying or partial application is the process of generating a function from another function or method by providing only some of the arguments. This is useful for saving typing, and when you want to pass a callback to another function.
Suppose you want a function that lets you extract substrings from "Hello, World"
easily. The classical way of doing that is writing your own function:
sub f(*@a) { substr('Hello, World', |@a) }
Currying with assuming
Perl 6 provides a method assuming
on code objects, which applies the arguments passed to it to the invocant, and returns the partially applied function.
my &f := &substr.assuming('Hello, World');
Now f(1, 2)
is the same as substr('Hello, World', 1, 2)
.
assuming
also works on operators, because operators are just subroutines with weird names. To get a subroutine that adds 2 to whatever number gets passed to it, you could write
my &add_two := &infix:<+>.assuming(2);
But that's tedious to write, so there's another option.
Currying with the Whatever-Star
my &add_two := * + 2; say add_two(4); # 6
The asterisk, called Whatever, is a placeholder for an argument, so the whole expression returns a closure. Multiple Whatevers are allowed in a single expression, and create a closure that expects more arguments, by replacing each term *
by a formal parameter. So * * 5 + *
is equivalent to -> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">a, </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">a</span><span class="mpunct">,</span></span></span></span>b { <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>∗</mo><mn>5</mn><mo>+</mo></mrow><annotation encoding="application/x-tex">a * 5 + </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.4653em;"></span><span class="mord mathnormal">a</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">∗</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="base"><span class="strut" style="height:0.7278em;vertical-align:-0.0833em;"></span><span class="mord">5</span><span class="mord">+</span></span></span></span>b }
.
my $c = * * 5 + *; say $c(10, 2); # 52
Note that the second *
is an infix operator, not a term, so it is not subject to Whatever-currying.
The process of lifting an expression with Whatever stars into a closure is driven by syntax, and done at compile time. This means that
my $star = *; my code=code = code=star + 2
does not construct a closure, but instead dies with a message like
Can't take numeric value for object of type Whatever
Whatever currying is more versatile than .assuming
, because it allows to curry something else than the first argument very easily:
say ~(1, 3).map: 'hi' x * # hi hihihi
This curries the second argument of the string repetition operator infix x
, so it returns a closure that, when called with a numeric argument, produces the string hi
as often as that argument specifies.
The invocant of a method call can also be Whatever star, so
involves a closure that calls the uc
method on its argument.
MOTIVATION
Perl 5 could be used for functional programming, which has been demonstrated in Mark Jason Dominus' book Higher Order Perl.
Perl 6 strives to make it even easier, and thus provides tools to make typical constructs in functional programming easily available. Currying and easy construction of closures is a key to functional programming, and makes it very easy to write transformation for your data, for example together with map
or grep
.