The Current Sub in Perl 5.16

| 9 Comments

Recursion is one of those computer science ideas that seems so difficult to understand before you get it, and then seems so easy after you understand it that you can't remember not understanding it.

A anonymous function is one of those computer science ideas that makes no sense until it makes sense, and then you understand the beauty (and the horror) of the Von Neumann architecture. (Long story short: computers don't care about the names of things. People do.)

When you combine those two ideas, you get things like the Y combinator, in which you jump through hoops to create an anonymous function which recurses to itself while remaining anonymous.

If you're the kind of pragmatic joker that Perl tends to attract, you might say to yourself "Wow, those Scheme hackers are crazy. Just do it in one step of Perl 5 like this:"

my $func;
$func = sub
{
    my $factor = shift;
    return $factor > 1
        ? $func->( $factor - 1 ) * $factor
        : 1;
};

... which preserves the anonymity of the function and allows recursion at the cost of a memory leak. (Weakrefs fix this, but are you going to remember to do that?)

(At this point, Python fans will say "Just stick a name on that function. There's no reason not to name everything." They may have a point; I went through a phase when I was a kid where I used my parents's embossing label maker whenever I could. The problem with that is that the adhesive leaves a sticky residue on everything. No amount of enforced whitespace can fix that.)

Perl 5.16 fixes this situation.

If you use 5.016; or use feature 'current_sub';, you enable the __SUB__ builtin, which is a reference to the function currently executing.

Suppose you have a tree structure representing articles, something like:

my $tree =
[
    { state => 'READ',   id  => 1 },
    { state => 'UNREAD', id  => 2 },
    [
        { state => 'READ', id  => 3 },
        { state => 'READ', id  => 4 },
        [
            { state => 'UNREAD', id  => 5 },
            { state => 'READ',   id  => 6 },
        ],
    ],
];

Suppose you want to traverse this structure looking for articles in the UNREAD state. In Perl 5.16, you might write something like:

sub traverse
{
    my ($root, $comparator) = @_;

    my @items;
    for my $element (@$root)
    {
        if (ref $element eq 'ARRAY')
        {
            push @items, __SUB__->( $element, $comparator );
        }
        else
        {
            push @items, $element if $comparator->( $element );
        }
    }

    return @items;
}

... as a starting point. Sure, there's no need in the code as it exists now to use __SUB__ instead of the hard-coded traverse(), but consider two pragmatic arguments for anonymous recursive functions. First, the two-step code to make a lexical recursive function (first, declare the lexical variable; in the next statement, use that lexical binding to recurse) has the danger of memory leak and it looks odd. The Y combinator code in Perl is worse. Clarity suffers.

Second, the drawback of using a named function is that that function is available in the namespace. If you write mostly object oriented code (as I do) with hlper functions (as I do), the fact that Perl borrowed Python's misfeature of exposing everything in a namespace as a method means that any named function may be exploitable as a method.

Anonymous functions ameliorate that danger.

Anonymous recursive functions (where recursion is appropriate) are a tool wielded with wisdom.

Anonymous recursive functions with trivial syntactic support (the implementation in the core is next to trivial) are a boon to Perl's pragmatism.

(If you're using an older version of Perl 5, see Sub::Current for a workalike.)

9 Comments

my $func;
$func = sub
{
    my $factor = shift;
    return $factor > 1
        ? $func->( $factor, $factor - 1 ) * $factor
        : 1;
};
This is not the Y combinator, if that's what you were going for.

That will leak the $func coderef because it holds a reference to itself.

The recursive call seems likely to trigger an infinite loop... maybe $func->($factor - 1) would be better.

The consideration about the memory leak is very interesting, am I right to assume that it is worked around by the Y combinator?

Of course! I didn't want to show a Y combinator in Perl here because it's a little mind bending until it clicks and because the point is you don't need the Y combinator in Perl now.

Oh, and chromatic: "a little"? :-)

But your code doesn't even work. See Flavio's comment.

You're right, thanks. I set this entry aside, then came back and hit publish before I finished editing.

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide

Categories

Pages

About this Entry

This page contains a single entry by chromatic published on May 21, 2012 11:10 AM.

Programming Breaks Things was the previous entry in this blog.

Toward Coding Without Conditionals is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.


Powered by the Perl programming language

what is programming?