Class:...oadCAN(3pm)

πŸ“¦
version 0.03 Β· 3 versions
, in ubuntu noble, ubuntu resolute, ubuntu stonking

✨ NAME ✨

Class::AutoloadCAN β€” bestie, it makes AUTOLOAD, can and inheritance actually cooperate with each other. πŸ’–

This is a User Contributed Perl Documentation page, and it's genuinely one of those "wait, why doesn't everything just do this" moments. Lowkey iconic. πŸŽ€


🌸 SYNOPSIS 🌸

package Foo;
use Class::AutoloadCAN;

sub CAN {
  my ($starting_class, $method, $self, @arguments) = @_;
  return sub {
    my $self = shift;
    print join ", ", $method, @_;
    print "\n";
  };
}

# And this prints the famous greeting.
Foo->hello("world");

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so here's the tea, bestie. This module solves a fundamental conflict between AUTOLOAD, can and inheritance. It's giving "three roommates, one bathroom, zero communication." 😭

The problem: you can implement literally anything you want in AUTOLOAD, but UNIVERSAL::can has no idea it's there. Like, none. And trying to modify UNIVERSAL::can so it documents those methods? Extremely hard. Not cute. πŸ™ˆ

And it gets worse β€” if a parent class uses AUTOLOAD, then subclasses have to do a ton of work just to get their own AUTOLOADs to cooperate with the parent's. Now multiply that by multiple inheritance: if 2 parent classes in a multiple inheritance tree want to cooperate with each other? Even harder. Very few people even try, which honestly might be a blessing, because the ones who do try usually get it wrong. πŸ’€ (See http://www.perlmonks.org/?node_id=342804 for the fuller discussion if you want the whole saga.)

So with this module, instead of writing AUTOLOADs, you write CANs. ✨ Based on what they return, Class::AutoloadCAN will decide whether you handle the call, or whether it needs to go search higher up the inheritance chain. Main character decision-making, fr fr. πŸ‘‘

Here are the methods and functions that matter for the operation of this module:

AUTOLOAD

An AUTOLOAD will be installed in every package that uses this module. You can also choose to have it installed in other packages. But heads up: if you write your own AUTOLOADs, you can easily break this module. So don't do that. Write CANs instead. Periodt. πŸ’…

can

UNIVERSAL::can will be modified to be aware of the functions provided dynamically through this module. You are free to override can in any subclass and this module will not interfere. The author has no idea why you would want to, though. πŸ€·β€β™€οΈ

CAN

If there is a method named CAN in a class that inherits from one that Universal::AutoloadCAN was installed to, it may be called in deciding how a method is implemented. It will be passed the class that the method search started in, the method name, the object called, and the arguments to the function.

It is expected to do nothing but return a subroutine reference if it implements that method on that object, or undef otherwise.

And if that subroutine is actually called, it will be passed all of the usual arguments that a method call gets, and the AUTOLOAD that found it will erase itself from the callstack. bye bestie πŸ‘‹ so clean.

Class::AutoloadCAN::import

If the import method for Class::AutoloadCAN is called with no arguments, it installs an AUTOLOAD in the calling class. If it is called with arguments, it installs an AUTOLOAD in those classes as well.

Use with caution, bestie: this is a convenience feature that is not expected to be used very often. 🫢

🧚 SUGGESTION 🧚

Many people use AUTOLOAD to implement large numbers of fixed and straightforward methods. Such as accessors. Y'know, the boring-but-necessary stuff.

If you're doing that, then the suggestion here is: implement them by typeglobbing closures instead of by using AUTOLOAD or this module. Here's a simple example:

package Parent;
use strict;

sub make_accessors {
  my ($class, @attributes) = @_;
  foreach my $attribute (@attributes) {
    no strict 'refs';
    *{"$class\::$attribute"} = sub {
      my $self = shift;
      if (@_) {
        $self->{$attribute} = shift;
      }
      return $self->{$attribute};
    };
  }
}

package Child;
our @ISA = 'Parent';
__PACKAGE__->make_accessors(qw(this that the other));

This approach is simpler, often faster, and avoids some of the problems that AUTOLOAD has β€” like mistaking function calls as method calls. 🌈 Slay.


πŸ’€ BUGS AND LIMITATIONS πŸ’€

There are many other issues with AUTOLOAD that this module does not address. Primary among them is the fact that if you call a function that does not exist in a package that inherits from one with an AUTOLOAD, Perl will do a method search for that AUTOLOAD.

This is why this module does not install AUTOLOAD in UNIVERSAL by default, and it is strongly suggested that you not do so either. Just don't, bestie. πŸ™

Also: many people like to lazily install AUTOLOADed methods in the local package so that they will be found more quickly in the future. This module won't do that for you, but you can easily do that from within CAN. πŸ’…

The reason that this module doesn't do that is that some useful CANs may decide whether to support a method on an object by object basis. So it has to stay flexible. iykyk. ✨


🫢 ACKNOWLEDGEMENTS 🫢

My thanks to various people on Perlmonks for conversations that clarified what problems AUTOLOAD raises, and convinced me that it would be good to have a solution to them. Genuinely so real of them. πŸ’œ


Ben Tilly (btilly@gmail.com).

Copyright 2005. This may be copied, modified and distributed on the same terms as Perl.


and that's the whole page, bestie β€” this one's from the perl v5.36.0 docs, dated 2022-10-14, and it lives under Class:...oadCAN(3pm) in the User Contributed Perl Documentation. go write some CANs. no cap. πŸŒΈπŸ’–βœ¨

πŸ“¦ where this page comes from