Class::Struct has been a core module for ages. (Previously it was Class::Template, but a great renaming occurred 13 years ago.) If you've never seen it before, it might remind you a little bit of Moose:

package Cat; use Class::Struct; struct( name => '$', age => '$', diet => '$' );

You don't get all of the benefits of Moose, but you do get attributes and accessors. You also get a default constructor.

Of course, the default constructor reads something like:

{ package Cat; use Carp; sub new { my ($class, %init) = @_; $class = __PACKAGE__ unless @_; ... } ... }

If that emboldened line is curious to you, it's curious to me too. I saw a note in one of the test files somewhere suggesting that the purpose of this was to allow you to write:

package Cat; my $cat = new();

I don't know why you'd do that, however. In what kind of object design does it make sense to create objects of a class from within that class? (That seems like a violation of responsibilities to me.) You can also write:

package NotCat; my $cat = Cat::new();

... though that's exceedingly fragile. For one thing, it implies that you could also write RobotCat::new() —assuming that RobotCat extends Cat , but avoiding method dispatch for calling a constructor means that RobotCat had better provide its own new() which behaves as a function as well as a method. (Even if you somehow convinced the subclass to inherit the superclass's function through some sort of exporting scheme, the hardcoded __PACKAGE__ would hurt.)

Hardcoding a method dispatch as a function dispatch means that the maintainers of Cat are not free to change how Cat provides its constructor, much for the same reason.

Woe unto you if there's an inherited AUTOLOAD somewhere.

I realize that in 1994 or 1995, people who wrote OO code in Perl 5 might have had familiarity with OO in C++ (where this syntax makes a little more sense) or, perhaps, Java where the indirect constructor call (the my Cat $cat = new Cat; is prevalent), but the benefit of hindsight is that experienced Perl 5 programmers can look back on this API a decade later and cringe at its potential for misuse.

If you're lucky, everything will go right—but what kind of a defensive programmer relies on luck when designing an API?