Каковы правила повторной привязки?

Раку иногда запрещает повторную привязку; обе следующие строки

      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;

Таким образом, похоже, что в настоящее время повторная привязка разрешена к любому имени, независимо от сигилы или ее отсутствия, если выполняется одно из следующих условий:

  1. Имя имеет какое-либо явное ограничение типа (включая Any и ограничения типа, наложенные @ или % сигилы), или
  2. При повторной привязке используется полное имя.

Это повторное связывание в настоящее время происходит как для объявленных переменных, так и для параметров и включает параметры, которые не являются rw или copy. Он даже, как показывает последний пример, допускает повторное связывание способами, которые (кажется?) Нарушают лексическую область видимости. (Этот пример был основан на тесте Roast , помеченном комментарием -- legal?, что говорит о том, что, возможно, я, по крайней мере, не единственный, кто нахожу такое поведение удивительным! Хотя тест повторно связывает is dynamic переменная - в некотором смысле приведенное выше поведение даже более удивительно).

Насколько я могу судить, единственными именами, которые нельзя повторно связать с использованием одного из этих подходов, являются имена, объявленные как constant.

Итак, четыре вопроса:

  1. Правильно ли я описываю текущее поведение?
  2. Это поведение правильное / преднамеренное / соответствует спецификации? (Несмотря на наличие S03-связывания , я обнаружил очень мало о повторном связывании).
  3. Если такое поведение не является преднамеренным, каковы должны быть правила повторной привязки?
  4. Есть ли способ сказать Раку «не переустанавливать это имя на новое значение, нет-правда-я-имею в виду»?

(Этот вопрос заменяет мой предыдущий вопрос , который я задал до того, как понял, насколько легко повторно связать имя; я закрываю его в пользу этого. Другой связанный с этим вопрос: есть ли цель или выгода в запрете переменных без знака из 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.

Другие вопросы по тегам