http://www.perlmonks.org?node_id=1019860


in reply to Re^6: Moose and default values
in thread Moose and default values

Sure you can always hack togther something, I was more looking for a way to do such things declaratively...

But your Haskell-ripoff is a at least a step forward - thanks.

Replies are listed 'Best First'.
Re^8: Moose and default values
by tobyink (Canon) on Feb 20, 2013 at 23:15 UTC

    With enough hacking you can make anything look declarative!

    use v5.14; use strict; use warnings; # Ugly hack code # package MooseX::AttributeShouldIgnoreFalse { BEGIN { $INC{'MooseX/AttributeShouldIgnoreFalse.pm'} = __FILE__ }; use Sub::Exporter -setup => { exports => ['attribute_should_ignore_false'], groups => { default => ['attribute_should_ignore_false'], }, }; sub attribute_should_ignore_false { my @attrs = @_; my $meta = (scalar caller)->meta; $meta->add_around_method_modifier(BUILDARGS => sub { my $orig = shift; my $self = shift; my $parm = $self->$orig(@_); $parm->{$_} || delete $parm->{$_} for @attrs; $parm; }); } } # Lovely declarative code # package Foo { use Moose; use MooseX::AttributeShouldIgnoreFalse; has foo => (is => 'ro', default => 'whatever'); attribute_should_ignore_false 'foo'; } my $value = 'hello'; print Foo->new(foo => $value)->dump; $value = ''; print Foo->new(foo => $value)->dump;
    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
      Were getting here :-)

      Ideally I would want to declare everything at the attriute-level, e.g.:

      has foo => (is => "ro", default => { value => "whatever", when => "fal +se" });
      Can you do that as well?

      I am not so much interested in the feature itself, but it is interesting to see some examples of Moose-metaprogramming at work...

      Thanks!

        In this case what I'd suggest is a custom type constraint.

        use Moose::Util::TypeConstraints; subtype 'TrueStr', as 'Str', where { !!$_ }; coerce 'TrueStr', from 'Any', via { $_ || "whatever" }; has foo => (is => "ro", isa => "TrueStr", coerce => 1);

        Then any value which is not a TrueStr will be coerced into one via the code $_ || "whatever".

        Of course, this also applies to accessors, not just the constructor. (Not a problem for a read-only attribute though!) And also, it means that if you've got a bunch of attributes like this, each with different defaults, you need to define a type constraint for each one.

        The alternative - attribute metaprogramming - starts off seeming simple enough, but becomes more complicated when you start thinking about immutability. (Attribute metaprogramming can be a real bitch when it touches on immutability.)

        A compromise would be to hook has to automatically build per-attribute type constraints and coecions. I submitted a patch to MooseX::AttributeShortcuts for precisely this feature some months ago, but it was only partly accepted: per-attribute constraints are now supported, but not coercions.

        package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name