12. rw, copy, readonly и raw в Perl 6

Вчера мы говорили об обратном слеше, который связывает парметры функции с данными, переданными при вызове. Сегодня мы продолжаем тему и поговорим о тех модификаторах (trait), которыми могут быть снабжены параметры.

Начнем с простого кода:

sub f($x) {
    $x++;
    say $x;
}

my $v = 42;
f($v);
say $v;

Этот код вполне бы работал в Perl 5.20 (если добавить use feature 'signatures'), но в Perl 6 он завершается с ошибкой:

Cannot resolve caller postfix:<++>(Int); the following candidates
match the type but require mutable arguments:
    (Mu:D $a is rw)
    (Int:D $a is rw)

The following do not match for other reasons:
    (Bool:D $a is rw)
    (Bool:U $a is rw --> Bool::False)
    (Mu:U $a is rw)
    (Num:D $a is rw)
    (Num:U $a is rw)
    (int $a is rw)
    (num $a is rw --> num)
  in sub f at args-1.pl line 2
  in block  at args-1.pl line 7

Сообщение об ошибке большое, но не сообщает о главном: по умолчанию аргументы функции разрешены только для чтения. Если убрать инкремент $x++, то все заработает:

42
42

Параметры в сигнатуре функции могут содержать дополнительные пометки со словом is. Если пометки нет, это все равно что есть is readonly, и, конечно же, понятно, что такую переменную изменить нельзя:

sub f($x is readonly) {
    # $x++;
    say $x;
}

Хотите что-то менять — передавайте копию переменной (is copy) или разрешайте ее изменять явно (is rw).

sub f($x is copy) {
    $x++;
    say $x;
}

sub g($x is rw) {
    $x++;
    say $x;
}

my $v = 42;
f($v);  # 43
say $v; # 42

g($v);  # 43
say $v; # 43

Понятно, что если передать константу, то изменить ее не получится:

f(42); # 43
g(42); # ошибка

Ошибка сообщает о том, что функция получила целое значение вместо изменяемой переменной (вполне себе сочетаемые слова).

Parameter '$x' expected a writable container, but got Int value
    in sub g at args-1.pl line 6
    in block  at args-1.pl line 19

Наконец, атрибут is raw это то же что и \, но только при этом переменные не лишаются сигила:

sub h($x is raw) {
    $x++;
    say $x;
}

my $n = 42;
h($n);   # 43
say $n;  # 43
# h(42); # ошибка

Как видите, возможностей весьма много, но это лишь часть того, на что способны сигнатуры функций в Perl 6.

8 thoughts on “12. rw, copy, readonly и raw в Perl 6”

    1. Raw может привязываться к значениям, а rw нет:

      sub f($x is raw) {
          say $x.WHAT;
      }
      
      my $n = 42;
      f($n);
      f(42);
      
      
      sub g($y is rw) {
          say $y.WHAT;
      }
      
      my $m = 42;
      g($m);
      # g(42); # error
      
      1. $ perl6 raw.pl 
        (Int)
        (Int)
        (Int)
        Parameter '$y' expected a writable container, but got Int value
          in sub g at raw.pl line 10
          in block  at raw.pl line 16
        

        Сигнатуру как раз «проходит» 🙂 Ошибка в рантайме.

  1. Я наверно плохо выразился. 42 в рантайме не проходит сигнатуру, даже несмотря на то, что сабрутина не пытается поменять значение.

    1. Ну да, это происходит во время попытки привязать (:=) параметр:

      # Binds a single parameter.
      sub bind_one_param($lexpad, $sig, $param, int $no_nom_type_check, $error,
                          int $got_native, $oval, int $ival, num $nval, str $sval) {
      
      . . .
      
      elsif $is_rw {
                  if nqp::isrwcont($oval) {
                      nqp::bindkey($lexpad, $varname, $oval) if $has_varname;
                  }
                  else {
                      if nqp::defined($error) {
                          my %ex := nqp::gethllsym('perl6', 'P6EX');
                          if nqp::isnull(%ex) || !nqp::existskey(%ex, 'X::Parameter::RW') {
                              $error[0] := "Parameter '$varname' expected a writable container, but got an " ~
                                  ~ $oval.HOW.name($oval) ~ " value";
                          } else {
                              $error[0] := { nqp::atkey(%ex, 'X::Parameter::RW')($oval, $varname) };
                          }
                      }
                      return $BIND_RESULT_FAIL;
                  }
              }
      

      Хотя наверное можно было отловить и при компиляции.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *