Связывание личных атрибутов: nqp :: bindattr vs: = - PullRequest
0 голосов
/ 02 октября 2018

Я пытаюсь выяснить, как операция связывания работает с атрибутами и чем она отличается от nqp::bindattr.Рассмотрим следующий пример:

 class Foo {
     has @!foo;

     submethod TWEAK {
         my $fval = [<a b c>];
         use nqp;
         nqp::bindattr( nqp::decont(self), $?CLASS, '@!foo',
         #@!foo :=
             Proxy.new(
                 FETCH => -> $ { $fval },
                 STORE => -> $, $v { $fval = $v }
             )
         );
     }

     method check {
         say @!foo.perl;
     }
 }

 my $inst = Foo.new;
 $inst.check;

Он печатает:

$["a", "b", "c"]

Замена nqp::bindattr оператором привязки из комментария даетправильный вывод:

["a", "b", "c"]

Точно так же, если foo является общедоступным атрибутом и используется аксессор, выходные данные также будут правильными из-за деконтеризации, происходящей в пределахаксессор.

Я использую аналогичный код в моем AttrX::Mooish модуле, где использование := усложнит реализацию.Пока что nqp::bindattr работал хорошо для меня, пока не возникла вышеуказанная проблема.

Я пытался отследить внутренности Ракудо в поисках реализации :=, но пока безуспешно.Я хотел бы попросить совета о том, как имитировать оператор или где в источнике искать его реализацию.

1 Ответ

0 голосов
/ 02 октября 2018

Прежде чем я углублюсь в ответ: большинство вещей в этом посте определяются реализацией, и реализация может определять их по-другому в будущем.

Чтобы выяснить, во что что-то (наивно) компилируется подRakudo Perl 6, используйте опцию --target=ast (perl6 --target=ast foo.p6).Например, связывание в:

class C {
    has $!a;
    submethod BUILD() {
        my $x = [1,2,3];
        $!a := $x
    }
}

Получается как:

                              - QAST::Op(bind)  :statement_id<7>
                                - QAST::Var(attribute $!a) <wanted> $!a
                                  - QAST::Var(lexical self) 
                                  - QAST::WVal(C) 
                                - QAST::Var(lexical $x)  $x

При переключении на @!a как здесь:

class C {
    has @!a;
    submethod BUILD() {
        my $x = [1,2,3];
        @!a := $x
    }
}

Выходитas:

                              - QAST::Op(bind)  :statement_id<7>
                                - QAST::Var(attribute @!a) <wanted> @!a
                                  - QAST::Var(lexical self) 
                                  - QAST::WVal(C) 
                                - QAST::Op(p6bindassert) 
                                  - QAST::Op(decont) 
                                    - QAST::Var(lexical $x)  $x
                                  - QAST::WVal(Positional) 

Большая разница здесь заключается в инструкции decont, и она будет принимать содержимое Proxy, вызывая его FETCH, поэтому контейнеризация исчезла.Таким образом, вы можете повторить поведение, вставив nqp::decont вокруг Proxy, хотя это скорее вызывает вопрос о том, что делает Proxy, если правильный ответ получен без него!

Оба := и = скомпилированы с использованием анализа случая (а именно, посмотрев на то, что находится слева).:= работает только для ограниченного диапазона простых выражений слева;это явно оператор низкого уровня.Напротив, = возвращается к вызову sub, если анализ случая не предлагает более эффективную форму для генерации, хотя в большинстве случаев он управляет чем-то лучшим.

Анализ случая для:= вставляет decont, когда целью является лексический или атрибут с символом @ или %, поскольку - на уровне Perl 6 - наличие элемента, связанного с @ или %, не имеет смысла,Использование nqp::bindattr поднимает уровень ниже семантики Perl 6, и поэтому возможно в конечном итоге получить Proxy, привязанный непосредственно там, используя это.Тем не менее, это также нарушает ожидания в другом месте.Не ожидайте, что это пойдет хорошо (но, похоже, вы все равно не хотите этого делать).

...