The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

autobox::Closure::Attributes - closures are objects are closures

VERSION

Version 0.03 released 16 May 08

SYNOPSIS

    use autobox::Closure::Attributes;

    sub accgen {
        my $n = shift;
        return sub { $n += shift || 1 }
    }

    my $from_3 = accgen(3);

    $from_3->n     # 3
    $from_3->()    # 4
    $from_3->n     # 4
    $from_3->n(10) # 10
    $from_3->()    # 11
    $from_3->m     # "CODE(0xDEADBEEF) does not close over $m"

WHAT?

The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil -- objects are merely a poor man's closures."

Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.

On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's objects." At that moment, Anton became enlightened.

DESCRIPTION

This module uses powerful tools to give your closures accessors for each of the closed-over variables. You can get and set them.

You can also call invoke methods defined in the package the coderef was created in:

    {
        package Foo;
        sub new {
            my $package = shift;
            my $x = shift;
            sub {
                $x *= 2;
            };
        }
        sub inc_x { my $self = shift; ++ $self->x }
    };
    
    my $foo = Foo->new(10);
    $foo->inc_x;     # $x is now 11; calls the method inc_x
    $foo->x = 15;    #           15; assigns to $x
    $foo->x(20);     #           20; assigns to $x
    $foo->();        #           40; runs the sub { }
    $foo->m;         # "CODE(0xDEADBEEF) does not close over $m"

Note that the coderef returned by sub { } was never blessed.

If Foo is used from a different file with use, then you'll need this boilerplate in your Foo.pm:

        sub import {
            my $class = shift;
            $class->autobox::import(CODE => 'autobox::Closure::Attributes::Methods');
        }

That enables autoboxing of code references in the program that uses the Foo.pm module.

You can get and set arrays and hashes too, though it's a little more annoying:

    my $code = do {
        my ($scalar, @array, %hash);
        sub { return ($scalar, @array, %hash) }
    };

    $code->scalar # works as normal

    my $array_method = '@array';
    $code->$array_method(1, 2, 3); # set @array to (1, 2, 3)
    $code->$array_method; # [1, 2, 3]

    my $hash_method = '%hash';
    $code->$hash_method(foo => 1, bar => 2); # set %hash to (foo => 1, bar => 2)
    $code->$hash_method; # { foo => 1, bar => 2 }

If you're feeling particularly obtuse, you could do these more concisely:

    $code->${\ '%hash' }(foo => 1, bar => 2);
    $code->${\ '@array' }

I recommend instead keeping your hashes and arrays in scalar variables if possible.

The effect of autobox is lexical, so you can localize the nastiness to a particular section of code -- these mysterious closu-jects will revert to their inert state after autobox's scope ends.

HOW DOES IT WORK?

Go ahead and read the source code of this, it's not very long.

autobox lets you call methods on coderefs (or any other scalar).

PadWalker will let you see and change the closed-over variables of a coderef .

AUTOLOAD is really just an accessor. It's just harder to manipulate the "attributes" of a closure-based object than it is for hash-based objects.

WHY WOULD YOU DO THIS?

    <#moose:jrockway> that reminds me of another thing that might be insteresting:
    <#moose:jrockway> sub foo { my $hello = 123; sub { $hello = $_[0] } }; my $closure = foo(); $closure->hello # 123
    <#moose:jrockway> basically adding accessors to closures
    <#moose:jrockway> very "closures are just classes" or "classes are just closures"

AUTHOR

Shawn M Moore, sartak@gmail.com

SEE ALSO

autobox, PadWalker

The "WHAT?" section is from Anton van Straaten: http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html

BUGS

    my $code = do {
        my ($x, $y);
        sub { $y }
    };
    $code->y # ok
    $code->x # CODE(0xDEADBEEF) does not close over $x

This happens because Perl optimizes away the capturing of unused variables.

Perl 5.8.9, 5.10.0, 5.12.0, and other earlier versions fail on the package method examples with the error: Can't modify non-lvalue subroutine call at /home/knoppix/lib/perl5/site_perl/5.8.9/autobox/Closure/Attributes.pm line 37. Change the mutators to instead read:

    sub inc_x :lvalue { ++ $_[0]->x; $_[0]->x }
    sub inc_y :lvalue { ++ $_[0]->y; $_[0]->y }

5.14 onward are smart enough to know that those accessors aren't actually called in lvalue context even though they're called from an lvalue method (AUTOLOAD).

COPYRIGHT AND LICENSE

Copyright 2007-2009 Shawn M Moore.

Copyright 2013 Scott Walters (scrottie).

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.