Your Mother has asked for the wisdom of the Perl Monks concerning the following question:

Using Types::Standard, MooX::HandlesVia, and Moo

#!/usr/bin/env perl { package FishTaco::Topping; use Moo; } { package FishTaco; use Moo; use MooX::HandlesVia; use Types::Standard qw( ArrayRef InstanceOf ); has _toppings => is => "rw", isa => ArrayRef[ InstanceOf['FishTaco::Topping'] ], default => sub { [] }, handles_via => "Array", handles => { add_topping => "push", toppings => "elements", }; } use strictures; use Path::Tiny; use Scalar::Util "blessed"; my $ft = FishTaco->new; $ft->add_topping( FishTaco::Topping->new ); # Sure. $ft->add_topping( path("/") ); # Wrong object type. $ft->add_topping( "OHAI" ); # Not even an object. print blessed($_) || "[n/a]", $/ for $ft->toppings; __END__ FishTaco::Topping Path::Tiny [n/a]

–but I expect the latter two calls to add_topping to throw an error since they “break” the isa of ArrayRef[ InstanceOf['FishTaco::Topping'] ]. I’m sure I’m doing something wrong or misunderstanding, but what? :P

(Update: title was badly worded.)

  • Comment on Trouble with, or misunderstanding of, Types::Standard ArrayRef[elements] enforcement in Moo
  • Select or Download Code

Replies are listed 'Best First'.
Re: Trouble or misunderstand of Types::Standard ArrayRef[elements] enforcement in Moo
by choroba (Cardinal) on Apr 28, 2019 at 20:30 UTC
    I fear it's the similar problem Perl6 has: add_topping runs push, but it doesn't change the array itself, so the isa check doesn't run.

    It's also documented in MooX::HandlesVia:

    methods delegated via the Moo handles interface are passed the attribue value directly. and there is no way to access the parent class. This means if an attribute is updated any triggers or type coercions WILL NOT fire.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Trouble with, or misunderstanding of, Types::Standard ArrayRef[elements] enforcement in Moo
by tobyink (Canon) on Apr 30, 2019 at 17:59 UTC

    Forget HandlesVia for now and consider this:

    my @toppings = ( FishTaco::Topping->new ); my $ft = FishTaco->new( _toppings => \@toppings ); push @toppings, "OHAI";

    You can push stuff onto the array without Moo checking that the stuff you're pushing makes any sense. This is because it only checks the array is valid in the constructor and in non-read-only attribute accessors. The exact same happens with Moose.

    Moose native traits give you the handles stuff for arrays, and Moose is smart enough to add type checking to push when you do this. MooX::HandlesVia is not quite so smart. (This is partly due to limitations in how Moo works.)

    Now try this:

    my @toppings = ( FishTaco::Topping->new ); tie @toppings, InstanceOf['FishTaco::Topping']; my $ft = FishTaco->new( _toppings => \@toppings ); push @toppings, "OHAI"; # this should die

    Yay?

    Now play around with trigger.

      Thanks. I ended up an around add_topping => …; but your suggestions are worth looking at I think and made me remember the types can be checked manually too. Thanks also for the great software.

        In the latest development releases of Types::Standard, you can do this:

        use Types::Standard is_InstanceOf => { of => "FishTaco::Topping", -as +=> "is_Topping" }; is_Topping( $something ); # returns a boolean

        If you've got Type::Tiny::XS installed, this will be an XS function, so pretty darn fast to call. I haven't done a lot of benchmarking on it, but I'd wager a lot faster than a blessed+isa check. If you don't, then it's still not going to be slow.

        (Older versions, you can do something like this for the same effect...

        BEGIN { require Types::Standard; *is_Topping = Types::Standard::InstanceOf->parameterize("FishTaco::T +opping")->compiled_check; };

        Newer versions just provide support for of in the import method.)