Приведение типов в атрибуте класса Perl6

Как и большинство вещей в Perl5, есть много способов создать класс, который поддерживает приведение настраиваемых типов для своих атрибутов. Вот простой, от ссылки на массив до хеша:

#!/usr/bin/env perl

package Local::Class {
  use Moo;
  use Types::Standard qw( HashRef ArrayRef );

  has set => (
    is => 'ro',
    coerce => 1,
    isa => HashRef->plus_coercions(
      ArrayRef, sub { return { map { $_ => 1} @{$_[0]} } },
    ),
  );
}

my $o = Local::Class->new({ set => [qw( a b b c )] });
# $o->set now holds { a => 1, b => 1, c => 1}

Я пытался перенести что-то подобное на Perl6, где мне кажется, что мне нужен способ принуждения Array к SetHash. Пока что единственный способ сделать это:

#!/usr/bin/env perl6

class Local::Class {
  has %.set;
  ## Wanted to have
  # has %.set is SetHash;
  ## but it dies with "Cannot modify an immutable SetHash"

  submethod TWEAK (:$set) {
    self.set = $set.SetHash;
  }
}

my $o = Local::Class.new( set => [< a b b c >] );
# $o.set now holds {:a, :b, :c}

Но мне кажется, что это не правильный способ сделать это, по крайней мере, из-за той крошечной детали, что передача нечетного списка в конструктор заставляет скрипт умереть.

Итак, как это делается в Perl6? Каковы рекомендуемые способы (потому что я уверен, что их несколько) для реализации приведения настраиваемого типа для атрибутов класса?


person jja    schedule 02.12.2016    source источник


Ответы (1)


TWEAK запускается после того, как объект был инициализирован BUILD, и именно здесь предоставление массива с нечетным номером взорвется.

Переместите приведение в BUILD время, и все должно работать как положено:

class Local::Class {
  has %.set;
  submethod BUILD (:$set) {
    %!set := $set.SetHash;
  }
}

Было бы неплохо, если бы вы могли использовать тип параметра coercing SetHash() в сочетании с автоматической инициализацией атрибута, но это не удастся, поскольку сигил является частью имени атрибута, а параметр не может быть %-sigill, если вы хотите принять неассоциативное типы.

Однако он отлично работает, если вместо этого вы используете атрибут $-sigilled:

class Local::Class {
  has $.set;
  submethod BUILD (SetHash() :$!set) {}
}

Как указывает @smls, следующий вариант

class Local::Class {
  has %.set is SetHash;
  submethod BUILD (:$set) {
    %!set = $set.SetHash;
  }
}

вероятно, тоже должно работать вместо того, чтобы умирать с Невозможно изменить неизменяемый SetHash.

В качестве обходного пути для SetHash, который нельзя назначить, вы можете использовать

class Local::Class {
  has %.set is SetHash;
  submethod BUILD (:$set) {
    %!set{$set.SetHash.keys} = True xx *;
  }
}
person Christoph    schedule 02.12.2016
comment
Я думаю, что он действительно должен работать и с has %.set is SetHash;, а Невозможно изменить неизменяемый SetHash - это просто ошибка Rakudo. - person smls; 02.12.2016
comment
submethod BUILD (SetHash() :$set){ %!set := $set } тоже работает - person Brad Gilbert; 02.12.2016