Вчера мы говорили об обратном слеше, который связывает парметры функции с данными, переданными при вызове. Сегодня мы продолжаем тему и поговорим о тех модификаторах (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.
Так чем всё-таки отличаются is raw и is rw?
Raw может привязываться к значениям, а rw нет:
О, спасибо! Получается, что константа просто не пройдёт сигнатуру rw.
Сигнатуру как раз «проходит» 🙂 Ошибка в рантайме.
Я наверно плохо выразился. 42 в рантайме не проходит сигнатуру, даже несмотря на то, что сабрутина не пытается поменять значение.
Ну да, это происходит во время попытки привязать (:=) параметр:
Хотя наверное можно было отловить и при компиляции.
В смысле юзер может отловить или компилятор мог бы отлавливать?
Компилятор.