Programming with Moose/Difficulties/Moose-w-Moose

After using Moose for a while you will eventually find yourself with an assortment of classes that you wish to reuse. Unfortunately, Moose won't make it all that easy to use those Moose classes from within Moose. Here are some difficult tasks[1] to do within Moose:

In these examples, MooseObj, is a Moose object.

  • isa => ArrayRef[ MooseObj ]
    • There is no syntactic sugar to facilitate in creating MooseObj(s).
    • Not easily possible to coerce from $self->MooseObj( [{constructor_args1}, {constructor_args2}] ).
  • isa => 'MooseObj'
    • No syntactic Autoboxing of the MooseObj, you can't do the following: $self->foo->new( {constructor_args} ).
    • No implicit way to bypass the creation of MooseObj and set its accessors. more on this in One-to-one Auto-vivification.

One-to-one Auto-vivification

edit

What if you don't care about MooseObj, only the accessors inside? A child object might do no more than function as logical grouping for more accessors, as in the following example:

package Address;
use Moose;

has 'street' => ( isa => 'Str', required => 1, is => 'ro' );
has 'city'   => ( isa => 'Str', required => 1, is => 'ro' );
has 'zip'    => ( isa => 'Str', required => 1, is => 'ro' );
has 'state'  => ( isa => 'Str', required => 1, is => 'ro' );
package USProfile;
use Moose;

has 'workAddress' => ( isa => 'Address', is => 'ro' );
has 'homeAddress' => ( isa => 'Address', is => 'ro' );

Even though Moose very well seeks to replace and abstract away a lot of perl's base-types here in comparison to having an Address hash it makes the job difficult. Here are some of the obvious difficulties:

  • You can not skip the step of creating a new Address object
    • The syntax $USProfile->workAddress->street('1234 FooStreet'), is not valid
    • The syntax $USProfile->workAddress->{street} = '1324 FooStreet' would be (assuming workAddress was a hash.)
  • You might wish for the component-class Address to require its child-attributes, but there is no way to later disable those requires=>1, when the class is used as a child of another class.
    • The syntax has 'workAddress' => ( isa => 'Address', is => 'ro', default => sub{Address->new} ); is not valid.
    • This wouldn't be all that useful in this case because of the like-problem with is=>'ro'.

Proposed Solution

edit

THIS SECTION IS BEING RE-WRITTEN Now obviously something nifty should happen to facilitate the creation of email. Any work done here would do a lot to further Moose's application into a Token-based system. Such as XML, where you can easily model, a one level tree in Moose, and then pull that into to another Moose class for a multiple level document. The following is pseudo-code that is not implemented.

my $person = USProfile->new;
## Hack a handles new into the Email code, which calls its own constructor
my $email = $person->email->new({  (constructor)  })

## Still permit some sort of construction that suffices the required
## obviously a no-go if you have it embedded in an ArrayRef or HashRef
my $person => USProfile->new({state=>'TX', email=>{[moose_args=>]constructor args} }

Maybe there needs to be a set of AttributeHelpers for things that inherit from Moose::Object

Another way to accomplish this would be something like (provided by steven)

has hash => (
  is => "ro"
  , default => sub { blessed(shift)->new }
  , lazy => 1
);

my $h = Hash->new;
$h->hash->hash->hash;
 print $h->dump;'

There are a few issues with this:

  • blessed(shift)->new should be the value the isa=> instead.
  • It doesn't accommodate for isa's, that have required=>1, there must be some way to autovivify, or send constructor args.

Footnotes

edit
  • ^ Difficult or overly verbose.