Объект Moo расширяет порядок

#perl

#perl

Вопрос:

Приведите приведенный ниже код, похоже, что порядок, в котором вы устанавливаете объекты, имеет значение. Приведенный ниже код выведет один и тот же список для обоих объектов, хотя я ожидал бы другого списка для каждого, потому что list — это атрибут экземпляра, который создается во время сборки.

 package t;

use Moo;
use Types::Standard qw(ArrayRef);

my @list = qw/foo bar baz/;

has list => (
    is => 'rw',
    isa => ArrayRef,
    default => sub {@list}
);

1;
---
package u;

use Moo;
use Types::Standard qw(ArrayRef);
extends 't';

sub BUILD {
    my ($self) = @_;

    push @{$self->list()}, qw/apple banana/;
    return $self;
}
1;
---
#!perl

use Data::Printer;
use t;
use u;

my $u = u->new();
p $u->list();

my $t = t->new();
p $t->list();
  

Текущий вывод:

  [
    [0] "foo",
    [1] "bar",
    [2] "baz",
    [3] "apple",
    [4] "banana"
]
 [
    [0] "foo",
    [1] "bar",
    [2] "baz",
    [3] "apple",
    [4] "banana"
]
  

Ожидаемый результат:

  [
    [0] "foo",
    [1] "bar",
    [2] "baz",
    [3] "apple",
    [4] "banana"
]
 [
    [0] "foo",
    [1] "bar",
    [2] "baz"
]
  

Ответ №1:

Поскольку вы изменяете рассматриваемый массив, вам не нужна ссылка на массив, который вы используете по умолчанию @list , вы хотите сделать неглубокую копию [@list] .

 package t;

use Moo;
use Types::Standard qw(ArrayRef);

my @list = qw/foo bar baz/;

has list => (
    is => 'rw',
    isa => ArrayRef,
        builder =>
    default => sub { [@list] }
);

package u;

use Moo;
use Types::Standard qw(ArrayRef);
extends 't';

sub BUILD {
    my ($self) = @_;

    push @{$self->list()}, qw/apple banana/;
    return $self;
}

package main;

use Data::Printer;

my $u = u->new();
p $u->list();

my $t = t->new();
p $t->list();
  

Пока я этим занимаюсь, использование BUILD для изменения атрибута возможно, но не обязательно является лучшим. Вы можете использовать что-то вроде отложенного атрибута с методом builder, затем перегрузить этот метод в подклассе, ala

 package t;

use Moo;
use Types::Standard qw(ArrayRef);

my @list = qw/foo bar baz/;

has list => (
    is => 'rw',
    isa => ArrayRef,
    builder => '_build_list',
    lazy => 1,
);

sub _build_list {
  my $self = shift;
  return [@list];
}

package u;

use Moo;
extends 't';

sub _build_list {
  my $self = shift;
  my $list = $self->SUPER::_build_list();
  push @$list, qw/apple banana/;
  return $list;
}

package main;

use Data::Printer;

my $u = u->new();
p $u->list();

my $t = t->new();
p $t->list();