Question

Is there some way in Moose to specify that I want an attribute to have a specific type, but also allow there to be a null value (undef?).

For example I am writing a simple implementation of a Linked List and have a Node class where the next and prev pointers are required to be of type Node (this is probably what you would expect)

package Node;
{
   use Moose; 

   has 'value' => (
      is  => 'rw',
      isa => 'Any', # Nodes can be of any type
   );

   has 'prev' => (
      is        => 'rw',
      isa       => 'Node',
      predicate => 'has_prev',
   );

   has 'next' => (
      is        => 'rw',
      isa       => 'Node',
      predicate => 'has_next',
   );

}

But I was hoping to use a sentinel, empty node stored at the head of the list to mark the head, instead of an actual element of the list. So a list of elements [1, 2, 3] would actually look like:

EMPTY -> 1 -> 2 -> 3

I was hoping to be able to specify a empty value (like undef) for the next and prev pointers, but when I create an empty Node in my List class:

package List;
{
   use Moose;

   has 'head' => (
      is      => 'rw',
      isa     => 'Node',
      # empty head node
      default => sub { 
         Node->new( value => undef, next => undef, prev => undef ); 
      }, 
   );

Moose complains because undef is not of type Node.

Is there a way around this ?

Était-ce utile?

La solution

You can use the Maybe[type] syntax to allow the type or undef. For your example:

   has 'head' => (
      is      => 'rw',
      isa     => 'Maybe[Node]',
      # empty head node
      default => sub { 
         Node->new( value => undef, next => undef, prev => undef ); 
      }
   );

Autres conseils

The next:

use 5.014;
use warnings;

package Node {
   use Moose; 
   has 'value' => ( is  => 'rw');
   has 'prev' => ( is => 'rw', isa => 'Undef|Node', predicate => 'has_prev', default=>undef );
   has 'next' => ( is => 'rw', isa => 'Undef|Node', predicate => 'has_next', default=>undef );
}

package List {
   use Moose;
   has 'head' => ( is => 'rw', isa => 'Node', default => sub { Node->new() } );
}

package main;
use Data::Dumper;

my $list = List->new();
say Dumper $list;

prints:

$VAR1 = bless( {
                 'head' => bless( {
                                    'next' => undef,
                                    'prev' => undef
                                  }, 'Node' )
               }, 'List' );

The Moose::Manual::Types says for the basic hier:

      Undef     <---- undefined
      Defined
          Value
              Str
                  Num
                      Int
                  ClassName <---- Class name
                  RoleName

and later in the section TYPE UNIONS says:

Moose allows you to say that an attribute can be of two or more disparate types. For example, we might allow an Object or FileHandle:

has 'output' => ( is => 'rw', isa => 'Object | FileHandle', );

As others already says, here is a Maybe[Something] too, I haven't idea what is better, but the Something | SomethingOther looks more "perlish" (IMHO). ;)

The authors prefer Undef|Node over Maybe[Node].

has 'prev' => (
   is        => 'rw',
   isa       => 'Undef|Node',
   predicate => 'has_prev',
);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top