Каковы правила повторной привязки?
Раку иногда запрещает повторную привязку; обе следующие строки
sub f($a) { $a := 42 }
my \var = 'foo'; var := 'not-foo';
произвести ошибку времени компиляции:
===SORRY!=== Error while compiling
Cannot use bind operator with this left-hand side
Однако Raku позволяет выполнять повторную привязку во многих, многих ситуациях, в том числе во многих, которые стали для меня большим сюрпризом. Все последующие успешно повторно связываются; каждый
say
выходы
not-foo
.
my Any \a = 'foo';
say a := 'not-foo';
my Any $b := 'foo';
say $b := 'not-foo';
my @c := ('foo', 'foo');
say @c := ('not-foo', 'not-foo');
my @d is List = ('foo', 'foo');
say @d := ('not-foo', 'not-foo');
my %e := (:foo<foo>);
say %e := (:not-foo<not-foo>);
sub fn1(Any \a) { a := 'not-foo'; say a }
fn1 'foo';
sub fn2(Any $b) { $b := 'not-foo'; say $b }
fn2 'foo';
sub fn3(@c) { @c := ('not-foo', 'not-foo'); say @c }
fn3 ('foo', 'foo');
sub fn4(+@d) { @d := ('not-foo', 'not-foo'); say @d }
fn4 ('foo', 'foo');
sub fn5(@d is raw) { @d := ('not-foo', 'not-foo'); say @d }
fn5 ('foo', 'foo');
my ($one-foo, $two-foo) := ('foo', 'foo');
$one-foo := 'not-foo';
say $one-foo;
my \foo = 'foo';
say MY::<foo> := 'not-foo';
sub foo-fn { 'foo' }
MY::<&foo-fn> := { 'not-foo' }
say foo-fn;
my $absolutely-foo = 'foo';
sub fn6 { CALLER::<$absolutely-foo> := 'not-foo';}
fn6;
say $absolutely-foo;
Таким образом, похоже, что в настоящее время повторная привязка разрешена к любому имени, независимо от сигилы или ее отсутствия, если выполняется одно из следующих условий:
- Имя имеет какое-либо явное ограничение типа (включая
Any
и ограничения типа, наложенные@
или%
сигилы), или - При повторной привязке используется полное имя.
Это повторное связывание в настоящее время происходит как для объявленных переменных, так и для параметров и включает параметры, которые не являются
rw
или
copy
. Он даже, как показывает последний пример, допускает повторное связывание способами, которые (кажется?) Нарушают лексическую область видимости. (Этот пример был основан на тесте Roast , помеченном комментарием
-- legal?
, что говорит о том, что, возможно, я, по крайней мере, не единственный, кто нахожу такое поведение удивительным! Хотя тест повторно связывает
is dynamic
переменная - в некотором смысле приведенное выше поведение даже более удивительно).
Насколько я могу судить, единственными именами, которые нельзя повторно связать с использованием одного из этих подходов, являются имена, объявленные как
constant
.
Итак, четыре вопроса:
- Правильно ли я описываю текущее поведение?
- Это поведение правильное / преднамеренное / соответствует спецификации? (Несмотря на наличие S03-связывания , я обнаружил очень мало о повторном связывании).
- Если такое поведение не является преднамеренным, каковы должны быть правила повторной привязки?
- Есть ли способ сказать Раку «не переустанавливать это имя на новое значение, нет-правда-я-имею в виду»?
(Этот вопрос заменяет мой предыдущий вопрос , который я задал до того, как понял, насколько легко повторно связать имя; я закрываю его в пользу этого. Другой связанный с этим вопрос: есть ли цель или выгода в запрете переменных без знака из rebinding?, в котором обсуждаются некоторые компромиссы проектирования из предположения, что переменные без сигил не могут быть повторно связаны, в отличие от некоторых из приведенных выше примеров.)
3 ответа
Решительно неавторитетный ответ ...
Есть ли способ сказать Раку: «Не переустанавливайте это имя на новое значение, нет-правда-я-имею в виду»?
Насколько я могу судить до сих пор - просто путем тестирования вариантов, а не путем проверки обжарки или исходного кода компилятора - вы можете и должны объявить символ без сигил, и он не должен быть объявлен с помощью
my \symbol ...
:
constant foo = 42;
class bar {}
enum baz <a b>;
subset boo;
Насколько я могу судить, приведенные выше объявления постоянно привязывают свои символы к их объявленным значениям во время компиляции включающего их compunit и пытаются изменить эти символы, например
MY::<foo> := 99;
, молча игнорируются.
Обратите внимание, что использование
constant
не указывает, является ли связанное значение неизменным:
constant foo = [42];
foo[1] = 99;
say foo; # [42 99]
Если вам нужна абсолютная неизменяемость , абсолютно неизменной привязки недостаточно. Вы должны убедиться, что связанный объект / значение также абсолютно неизменяем.
Обратите внимание, что здесь объявляется идентификатор sigil'd, поэтому вы можете изменить то, к чему он привязан:
sub f { say 'foo' }
MY::<&f> := { say 'boo' }
f; # boo
Если такое поведение не является преднамеренным, каковы должны быть правила повторной привязки?
Я предполагаю, чего это стоит, так это то, что все так, как должно быть.
С момента написания моего комментария о том, что мою ментальную модель разрушили, увидев
my \foo ...
объявленные переменные без сигил с ограничением типа, которое повторно связывается, и теперь, видя, что даже без ограничения типа можно повторно привязать такие идентификаторы, ссылаясь на их символ через тайник, я задним числом пришел к выводу, что все это имеет для меня совершенный ракунианский смысл.
(Похоже, что мои прошлые утверждения о том, что бессигильные переменные обязательно являются SSA или, по крайней мере, SSB, были ошибочными. Похоже, что это так, только если они являются константами времени компиляции. Это имеет смысл, но я все равно буду придерживаться с просто "кажется"...)
Это поведение правильное / преднамеренное / соответствует спецификации? (Несмотря на наличие S03-связывания, я нашел удивительно мало о повторном связывании).
Официальное определение «спецификация» является обжаренным ( г epository о х LL с pecification т ресы).
Частью официального выпуска версии компилятора Rakudo является обеспечение того, чтобы она прошла обжарку.
Так что, если вы скомпилировали и запустили код с официально выпущенным Rakudo, поведение официально соответствует спецификации.
Правильно ли я описываю текущее поведение?
Вы предоставили код, который компилируется и запускается, поэтому по этому определению, которое, по-моему, является единственным разумным, этот код действительно правильно описывает текущее поведение, где «текущий» подразумевает версию используемого компилятора (что, в свою очередь, были протестированы с определенной версией roast, которая должна быть помечена тегом git для соответствия определенной официальной версии языка Raku).
Имя имеет любое явное ограничение типа (включая Any и ограничения типа, налагаемые символами @ или%), или
Я не думаю, что здесь должно быть уместно ограничение типа. Каждый контейнер в Raku имеет неявное или явное ограничение типа (даже если оно
Mu
). Создание возможности повторной привязки путем размещения Any перед ним - это ошибка.
Вместо этого я думаю, что правило состоит в том, что вы можете повторно привязать имя, если компилятор не знает, что оно доступно только для чтения.
Честно говоря, известны только следующие случаи:
- параметры блока / подпрограммы
- лексические субтитры (т.е.
&f
послеsub f ...
) - любое имя без знаков (
my \x = ...
,class A { ... }
,constant a = ...
)
Непоследовательное поведение, о котором я спрашивал в вопросе, было результатом ошибки Rakudo — Rakudo допускал повторную привязку в некоторых ситуациях, где этого не должно было быть. Эта ошибка была устранена в Rakudo/Rakudo#4536.
После этого решения правила перепривязки следующие:
- Бессигильные «переменные» не могут быть переназначены (и не могут быть переназначены, поэтому они на самом деле не являются «переменными»)
- Переменные с сигилом, как правило , могут быть переназначены, за исключением приведенного ниже исключения.
- Если сигилированная переменная является частью Signature, то она не может быть перепривязана, если только она не объявлена как перепривязываемая через
is copy
или жеis rw
черты .- Это относится к сигнатуре функции (таким образом
sub f($a) { $a := 42 }
незаконно) - Это также относится к сигнатурам, которые деструктурированы как часть объявления переменной с
:=
. например, вmy ($var1, $var2) := ('foo', 'bar')
, правая часть является подписью и, таким образом,$var1
а также$var2
не может быть восстановлен.
- Это относится к сигнатуре функции (таким образом
Применение этих правил означает, что все следующие перепривязки (которые были разрешены, когда был задан вопрос) теперь запрещены:
my Any \a = 'foo';
say a := 'not-foo';
sub fn1(Any \a) { a := 'not-foo'; say a }
fn1 'foo';
sub fn2(Any $b) { $b := 'not-foo'; say $b }
fn2 'foo';
sub fn3(@c) { @c := ('not-foo', 'not-foo'); say @c }
fn3 ('foo', 'foo');
sub fn4(+@d) { @d := ('not-foo', 'not-foo'); say @d }
fn4 ('foo', 'foo');
sub fn5(@d is raw) { @d := ('not-foo', 'not-foo'); say @d }
fn5 ('foo', 'foo');
my ($one-foo, $two-foo) := ('foo', 'foo');
$one-foo := 'not-foo';
say $one-foo;
И наоборот, применение этих правил означает, что некоторые из других повторных привязок, показанных в вопросе, разрешены (правильно):
my Any $b := 'foo';
say $b := 'not-foo';
my @c := ('foo', 'foo');
say @c := ('not-foo', 'not-foo');
my @d is List = ('foo', 'foo');
say @d := ('not-foo', 'not-foo');
my %e := (:foo<foo>);
say %e := (:not-foo<not-foo>);
Наконец, некоторые из примеров, показанных в вопросе, включали изменение содержимого (псевдо) package. Это позволяет перепривязки, которые в противном случае были бы запрещены приведенными выше правилами:
my \foo = 'foo';
say MY::<foo> := 'not-foo';
sub foo-fn { 'foo' }
MY::<&foo-fn> := { 'not-foo' }
say foo-fn;
my $absolutely-foo = 'foo';
sub fn6 { CALLER::<$absolutely-foo> := 'not-foo';}
fn6;
say $absolutely-foo;
Однако, как и в случае с использованием протокола метаобъектов для доступа к закрытым методам/атрибутам, использование псевдопакетов для нарушения обычных правил Raku должно быть крайним средством и не имеет таких же гарантий стабильности, как другие аспекты Raku.