in reply to Moose type question

You should take a look at MooseX::UndefTolerant, which is an extension to support a more liberal view on undefined values then the core Moose has. I can tell you right now that the Moose core will not be changing this behavior, but we have been discussing moving UndefTolerant into the core as an optional trait.

-stvn

Replies are listed 'Best First'.
Re^2: Moose type question
by ikegami (Patriarch) on Jun 14, 2010 at 20:52 UTC

    I can tell you right now that the Moose core will not be changing this behavior

    Nor should it. That's why Maybe[] and MooseX::UndefTolerant exist. The problem is that Maybe[] doesn't currently work with coercion, and that's the behaviour that should be changed.

      The problem is that Maybe[] doesn't currently work with coercion, and that's behaviour that should be changed.

      The type 'Maybe[Foo]' is different the type 'Foo' and Maybe[`a] is a parameterized type, and parameterized types do not support deep coercion like that. We have discussed the topic of deep coercion, but it is simply deemed too magical and prone to action-at-a-distance type issues.

      Along with MooseX::UndefTolerant, it is also possible to subtype a Maybe[`a] type and then add coercions to that.

      subtype 'MaybeDateTime' => as 'Maybe[DateTime]'; coerce 'MaybeDateTime' => from 'St' => via { ... };
      Of course your coercions would need to account for the possibility of undef, but that shouldn't be too hard.

      -stvn

        Thanks. The above test script passes all tests with the following definitions. The redundancy is annoying, but it'll do until I get around to making/finding a proper trait/extension.

        BEGIN { package Class; use strict; use warnings; use Moose; has val => ( is => 'rw', isa => 'Num', required => 1, ); use Moose::Util::TypeConstraints; coerce __PACKAGE__ => from 'Num' => via { __PACKAGE__->new(val => $_) }; subtype 'Maybe'.__PACKAGE__ => as 'Maybe['.__PACKAGE__.']'; coerce 'Maybe'.__PACKAGE__ => from 'Num' => via { __PACKAGE__->new(val => $_) }; } BEGIN { package Container; use strict; use warnings; use Moose; has always => ( is => 'rw', isa => 'Class', coerce => 1, default => sub { Class->new(val => 0) }, ); has maybe => ( is => 'rw', isa => 'MaybeClass', coerce => 1, default => undef, ); }
Re^2: Moose type question
by ikegami (Patriarch) on Jun 15, 2010 at 06:12 UTC

    Elsewhere, you posted http://article.gmane.org/gmane.comp.lang.perl.moose/1697

    which has two possible values, None, meaning no value, and Some(`a) meaning a value of the type `a

    That sounds like a union, so I tried it. Coercion works! ...but constraints don't?! Am I missing something?

    Simple demonstration:

    $ perl -Moose -e'has a=>(is=>"rw",isa=>"Undef",coerce=>1); __PACKAGE__ +->new(a=>"abc")' Attribute (a) does not pass the type constraint because: Validation fa +iled for 'Undef' failed with value abc at [snip backtrace] $ perl -Moose -e'has a=>(is=>"rw",isa=>"Undef|Undef",coerce=>1); __PAC +KAGE__->new(a=>"abc")' $ perl -Moose -e'has a=>(is=>"rw",isa=>"Undef|Undef",coerce=>0); __PAC +KAGE__->new(a=>"abc")' Attribute (a) does not pass the type constraint because: Validation fa +iled for 'Undef|Undef' failed with value abc at [snip backtrace]

    Full demonstration:

      That sounds like a union, so I tried it. Coercion works! ...but constraints don't?! Am I missing something?

      It is not a union, it is an algebraic data type, there is a HUGE difference in terms of type safety and soundness.

      Your second "Simple demonstration" seems to be a bug, I have brought it up in #moose-dev to discuss.

      UPDATE:
      Actually we figured out what is happening there, it is a fall through on an edge case. So if you look at this:

      perl -Moose -e'has a=>(is=>"rw",isa=>"Undef|Undef",coerce=>1); my $x = + __PACKAGE__->new(a=>"abc"); print $x->a'
      You will get Use of uninitialized value in print at -e line 3., which is happening because when Moose::Meta::TypeCoercion::Union attempts to coerce and doesnt find a proper coercion it returns 'undef'. Which in most cases would cause a type-check failure, but here actually passes the type check just fine.

      Funny, because this perfectly illustrates why I really don't like undef. In this case we are trying to use it as a "no value", but your test case is using it as a valid value itself. The two ways of using it do not mesh well at all.

      UPDATE(2):
      See also Semipredicate Problem.

      -stvn

        It is not a union, it is an algebraic data type

        I have no idea what that means. You described a type that could take one of two types of values, and that what ::Union does. I figured it would be a solution that falls within your mental model, and thus likely to work and to get promoted.

        Your second "Simple demonstration" seems to be a bug

        Filed as CPAN RT#58411

        Funny, because this perfectly illustrates why I really don't like undef.

        The problem has nothing to do with undef. The problem is sending control data in band. A value was reserved to mean "no coercion occurred". If the function had used zero instead of undef, it would suffer from the same bug.

        Undef is only a problem if you think it's ok for you and only you to use undef despite a long and continuing history of usefully using undef in Perl.

Re^2: Moose type question
by ikegami (Patriarch) on Jun 14, 2010 at 22:06 UTC

    As its name implies, UndefTolerant only tolerates undef temporarily, whereas Maybe indicates undef is a valid value.

    $o->maybe(undef); # Ok, valid value $o->tolerant(undef); # Dies, invalid value

    I presume this is intentional, so it's useless to me.