The need for SUPER

ferreira on 2007-12-28T17:01:31

Also known as "The trap of SUPER::method"

I never have grokked SUPER and what exactly it could do for me. Until I wrote Class::Constructor::Factory.

Class::Constructor::Factory is a helper module to prevent me to spend time writing constructors after using a module like Class::Accessor (and siblings) that gave me for free handy accessors/mutators for object fields. Those modules also gave me a constructor for free. But I am never happy with constructors without a way to specify defaults when initial field values are not given.

So C::C::F is a step towards a more automated way to get better constructors for free and, in a near future, to write classes with less effort. Actually, using the 0.001 version of the module looks like this:

package Foo;

use Class::Constructor::Factory; 
use base qw( Class::Accessor Class::Constructor::Factory );

__PACKAGE__->mk_constructor0({
  foo => 42,
  bar => 'fourty-two',
});
__PACKAGE__->mk_accessors(qw( foo bar ));

The thing is that C::C::F augments the actual constructor provided by the IS-A chain of the new package with handling of defaults. So a closure is built and then installed into &Foo::new to work out the wanted magic.

my $foo = Foo->new();
is( $foo->foo, 42 );
is( $foo->bar, 'fourty-two' );

That is rather straightforward and involved code which looked like this:

package Class::Constructor::Factory;

...

sub make_constructor0 {
  my $self = shift;
  ...

  # return a closure
  return sub {
    my $self = shift;
    ...
    return $self->SUPER::new->( $self, \%f ); # XXX the offender
  };
}

I must say that this code is deceptively simple and wrong. Because ->SUPER::new tried to invoke the &new of Class::Constructor::Factory superclasses (which does not exist). I understood why by reading the docs of SUPER and perlobj. The relevant quote is:

"It is important to note that SUPER refers to the superclass(es) of the current package and not to the superclass(es) of the object."

So even after the closure was installed into the Foo namespace, ->SUPER::new yet referred to the superclass of the package where the code was compiled, not doing what I wanted.

The solution is to use SUPER and then rewrite the offender code line as:

    return $self->super('new')->( $self, \%f );

After this, the &new is searched correctly among the superclasses of the object and everything is just fine.

This entry is just to remind me that ->SUPER::method and dynamic code generation (or equivalently dealing with closures in namespaces other than where they were compiled) is at least misleading. I don't claim to have understood all the issues SUPER may address, but I learned something. Thanks, chromatic! And maybe this trap may be turned off in the future by toogling a feature in a future Perl 5 version.


thanks

markjugg on 2007-12-28T17:37:51

This post was a helpful reminder about the pitfalls and features of SUPER.

Thanks!

    Mark

Thanks Simon Cozens!

chromatic on 2007-12-28T19:59:44

Simon Cozens is the original author of the module; I took it over from him a couple of years ago. He deserves the credit.