Hi Chris, thanks for your responses. Ive replied inline..

> -----Original Message-----
> From: Chris Prather [mailto:perig...@gmail.com]
> Sent: 14 January 2009 19:41
> To: Howe, Tom (IT)
> Cc: Sartak; Yuval Kogman; Stevan Little; moose@perl.org
> Subject: Re: Possible to use coerce and auto_deref?
>
> On Wed, Jan 14, 2009 at 1:06 PM, Howe, Tom (IT)
> <tom.h...@morganstanley.com> wrote:
> > I have something like this:
> >
> > subtype 'Thing'
> >  => as 'Object'
> >  => where { $_->isa('ThingClass') };
> >
> > coerce 'Thing'
> >  => from 'Str'
> >  => via { ThingClass->new ( xyz=> $_ ) };
> >
> >
> > has 'things' => (
> >  is=>'rw',
> >  isa=>'ArrayRef[Thing]',
> >  lazy=>1,
> >  coerce=>1,
> >  auto_deref=>1,
> >  default => sub { [] },
> > };
> >
> >
> > But if I pass in a list of strings to convert to Things, I
> get the error:
> >
> > "Cannot coerce without a type coercion"
> >
> > I've managed to get around it by defining a type 'ArrayRef[Thing]'
> >
> > Eg
> > Subtype 'ArrayRef[Thing]'
> >  => as 'ArrayRef'
> >  => where {
> >        foreach (@{$_} ....
> >     };
> >
> > Coerce 'ArrayRef[Thing]'
> >  => from 'ArrayRef'
> >
> > But this means
> > A) I'm essentially reimplementing ArrayRef each time.
> > B) It always coerces!
>
> isa => 'Arrayref[Thing]'  is setting up the subtype for you,
> so you don't need to do that explicitly You will need to
> create the coercion however, and yes every time you enable
> coerce => 1 Moose will look for an applicable coercion (for
> example: from ArrayRef to ArrayRef[Thing]) and apply it.


It seems like I need to define the subtype otherwise the coercion complains.

If I remove the subtype 'ArrayRef[Thing]' I get error of type:

Cannot find type 'ArrayRef[Thing]', perhaps you forgot to load it. at
...
        
Moose::Util::TypeConstraints::_install_type_coercions('ArrayRef[Thing]', 
'ARRAY(0x8ccfffc)') called at
...
        Moose::Util::TypeConstraints::coerce('ArrayRef[Thing]', 
'ArrayRef[HashRef]', 'CODE(0x8cb9418)') called at





>
> > It should ideally check before it coerces...
> >
> > Anything I can do to improve on this?
>
> What are you hoping it will check?
>
> Moose doesn't do any kind of "Deep Coercion" because the edge
> cases become far to messy to handle in an elegant way ... or
> at least so far as anybody has thought of. What you are doing
> seems to be the right solution ... with my comments about the
> "subtype ArrayRef[Thing]"
> statment being optionally implicit.



I guess in my mind it would work like this:

Does the value you are trying to set match the 'where' in
the type definition?
 Yes -> store it
 No  -> Does it match a coercion entry point?
        Yes -> coerce it, then store it.
        No  -> error.

Whereas Moose coerces first then applies the type check.

So in my example above, the coercion will be applied to every
Array you try to set, even if it is already matches the checks
in the type def. Given that the coercion might be expensive this
isnt ideal.
Is there a way to access the where=> sub from the type def from
within the coerce via=> sub of the coerce?

So you could have something like..

Coerce => 'ArrayRef[Thing]'
  via => {
    # check input type and only coerce if not matching type
    if ( 'ArrayRef[Thing]'->where($_) ) {
      return $_;
    } else {
      # fails type, do coerce.
      return [ map {  ..  } @{$_} ];
    }
 }






>
> > Also, I'd like to be able to declare something like
> >
> > has 'foo' => ( is=>'rw', delegate=> sub { Foo->instance }, handles
> > =>[qw/x y z/] )
> >
> > Where, if no value is passed in to foo() on construction,
> the accessor created for foo() will always trigger the
> delegate sub but will not store anything in the object in the
> way default does.
> >
> >
> > I tried this ..
> >
> > has 'foo' => ( is=>'rw', isa=>'Object', handles=> [qw/meth1 meth2
> > meth3/]);
> >
> > around 'foo' => sub {
> >  my ($next,$self,@args) = @_;
> >  if (@args) {
> >    return $self->$next(@args);
> >  } else {
> >    return $self->$next() || Foo->instance();  } };
> >
> > But got error:
> >
> > Cannot delegate meth1 to meth1 because the value of foo is
> not defined...
> >
> >
> > Is there some way to do this?
>
> Uh ... not if I'm understanding what you're asking for properly.
>
> You want an attribute accessor to sometimes store and Object,
> and sometimes just call a Class Method on some random class
> if the attribute isn't set? You'll have to write that yourself:
>
> has foo => ( is => 'rw', predicate => '_has_foo', handles =>
> [qw(one two three)] );
>
> around [qw(one two three)] => sub {
>     my ($next, $self) = (shift, shift);
>     return Foo->instance->$next(@_) unless $self->_has_foo;
>     return $self->$next(@_);
> };
>
> You could probably wrap this up in a MooseX module with some
> sugar if you find your using it a lot, but I don't understand
> why you'd want to do that in the first place.
>
> -Chris



I tried your example above but am getting the same error as before:

"Cannot delegate meth1 to meth1 because the value of foo is not defined at"

It seems that if you don't set the attribute, it wont be happy about using 
delgation to it.



In my scenario, I have lots loads of little objects that
I am pulling from a middleware layer and caching.

The objects need to have access to a default API/Connector object
(singleton) that can be used to find related objects.

* I don't want this API object to appear in the data of the object
 when you serialise it. *

Normally you would do the following:

package LilObject;

has 'APIconnector' => (
  isa=>'rw',
  default => sub { APIconnector->new() }
  handles => [qw/load reload save find_related find_parent .. /]
);

But here you get a $self->{APIconnector};

If I serialise this with Data::Dumper or Storable, I get the whole
of APIconnector too , which id like to avoid.
Given that APIconnector is a singleton, the object doesn't need
to maintain a reference to it.
There are ocassions when I may need to manually override the
default APIconnector object
and then it is fine that a reference to it is held.


So ideally...

  my $ob =  LilObject->new();
  $ob->save;   # triggers default APIconnectorSingleton

  print Data::Dumper($ob2)
  $VAR1 = bless( {
               }, 'LilObject' );  # no {APIconnector} visible



  my $ob2 = LilObject->new(APIconnector=> APIconnector->new( xyz=>1 );
  $ob->save;   # delegates to provided APIconnector

  print Data::Dumper($ob2)

  $VAR1 = bless( {
                  APIconnector => bless ( { xyz=>1 }, 'APIconnector )
               }, 'LilObject' );


In another scenario, id also like to dynamically choose the object
that is delegated to. So the 'around' method would suit that.

has 'foo' (is =>'rw', handles => [qw/x y z/] )
around 'foo' => (
  if (some clause) {
     return $Foos::a
  } else {
     return $Foos::b
  }
}

The problem seems to be that delegation errors if foo is not defined.


For now im using:

sub foo { MSDW::Oscar::Foo->new() };

#set up some delegators
foreach my $name (qw/Meth1 meth2 /) {
  method $name => () => sub { shift->foo->$name(@_) };
}

But this wont allow me to override the value with a provided one




Thanks, Tom



>
--------------------------------------------------------

NOTICE: If received in error, please destroy and notify sender. Sender does not 
intend to waive confidentiality or privilege. Use of this email is prohibited 
when received in error.

Reply via email to