in reply to Re: Class variables in Moose?
in thread Class variables in Moose?

Why does the Moose core not support class attributes?

Replies are listed 'Best First'.
Re^3: Class variables in Moose?
by stvn (Monsignor) on Jun 06, 2008 at 01:31 UTC
    Why does the Moose core not support class attributes?

    Well, two reasons really. To start with I don't like class attributes, IMO they are a broken concept. Practical ... sure, useful ... sometimes, but this does not make them any less broken.

    In a pure object model (which Moose strives to be, but can never attain in a p5 compatible world), there is no distinction between a class and an instance, because a class is just an instance of the class MetaClass. The knee-jerk approach is to make class attributes into the instance attributes of the given instance of MetaClass. But this is broken because you can't have several instances of MetaClass each with a different set of attributes (each class is going to want to define it's own class attributes right?) You wouldn't have several instances of your Foo class, each with a different set of attributes in it, right.

    Which brings me to the the second reason ...TIMTOWTDI. Different people use class level data and methods in different ways, here are some of the different ways I have pondered going about solving this.

    One way is to subclass each MetaClass such that you have a parallel hierarchy (Foo isa Bar, Foo::MetaClass isa Bar::MetaClass, etc etc), this means your classes inherit class attributes in a predictable way. But this only works with single inheritance languages and pretty much fails miserably the moment you introduce multiple inheritance because it produces an explosion of metaclass combinations, so obviously this is not an option for Moose.

    Another way is to do what MooseX::ClassAttribute does and create a parallel hierarchy separate from the MetaClass hierarchy, that contains the class data. This neatly sidesteps the issues of MI, by using has-a instead of is-a. I suspect, though I have no proof, that this would prove to break on some deep edge cases if pushed too far, but for the most part it Just Works so this is the one I suggest.

    Still another way is to use the inside-out-ish technique and store your data in a package scoped variable and simply write your own methods to deal with accessing it, etc. This is more manual and means no Moose helpers, but it gives you the choice of how you want to deal with inheritance of your class data (if you want to deal with inheritance at all). This is typically how I do class level data/method when I do them. Since that is not terribly often, I don't mind going "old school" with it.

    Another way is to consider class level data "out of band" data, and use a module like MooseX::MetaDescription to add "metadescriptions" to your class's MetaClass. This is how languages like Ruby do class attributes/methods, by having slots in the metaclass specifically for class attributes/methods. I always though that was ugly though (IMO of course, the ruby fanboys would surely disagree).

    And the last approach, which is a little wasteful, is to simply store that class level data as instance data that it not editable on a per-instance basis. You can do this with Moose like so:

    package Foo; use Moose; has 'bar' => ( is => 'ro', # access is read-only init_arg => undef, # you cannot set it via the constructor lazy => 1, # don't allocate a slot for it unless you acce +ss it default => 42, # and provide the default value );
    The data is not shared per-class though, so if you were to store something more complex like a HASH or ARRAY and to manipulate it directly (push @{$foo->bar} => 100; etc) then it would cease to be class level data. However if you treat it as purely static, this works just fine. You can even override it in a subclasses like so:
    package Bar; use Moose; extends 'Foo'; has '+bar' => (default => 100);
    Of course this is not ideal in many cases, but it is just OWTDI.

    So, basically, the short answer is: I couldn't decide which way was the right way, so I left it open to MooseX:: extensions to solve the problem.

    -stvn