More On Maintaining State with Traits

Ovid on 2007-05-09T17:14:39

On my O'Reilly blog, I wrote about problems with maintaining state with traits. The original traits model did not have a provision to handling state and this, I think, is a serious oversight. It's a limitation I've run into several times now and I need to fix it. Fortunately, I've not had a problem because the applications I've written using stateful trait methods have been applications with a very short runtime (such as test suites or small command-line programs), but in a persistent environment, when traits attempt to maintain state, they don't have any clean way of cleaning up when the object goes out of scope.

My thought was to try overriding DESTROY, if present, but this presents a difficult technical problem:

#!/usr/bin/perl -l

use strict;
use warnings;

{
    package Foo;

    sub new { bless {}, shift }

    sub DESTROY {
        use Data::Dumper;
        print Dumper \@_;
    }
}

my $foo = Foo->new;

__END__
# prints
$VAR1 = [
          bless( {}, 'Foo' )
        ];

So if I do that, I get an instance of Foo to play with. However, if I attempt to override:

#!/usr/bin/perl -l

use strict;
use warnings;

{
    package Foo;

    sub new { bless {}, shift }

    sub DESTROY {
        use Data::Dumper;
        print Dumper \@_;
    }
}

my $foo = Foo->new;
my $destroy = *Foo::DESTROY{CODE};
{
    no warnings 'redefine';
    *Foo::DESTROY = sub { print 'boo hoo'; $destroy->($foo) };
}
__END__
# prints
boo hoo
$VAR1 = [
          undef
        ];

Damn. No instance. I don't know that I can override DESTROY safely. The only other thought I can think of is specifying that a user state that they're using a 'stateful' trait and, if the trait is applied at compile or runtime, run the appropriate checks, and in either case, bless the class into an anonymous subclass which has its own DESTROY. That will let me use DESTROY, but "Yuck!"

Update: sheesh. I feel really stupid today. I can fix that with this:

    *Foo::DESTROY = sub { print 'boo hoo'; $destroy->(shift) };