(Этот ответ основан на ответе @ guifa и комментарии JJ.)
Идиома для использования в переменных чертах по существу $var.var.VAR
.
Хотя это звучит забавно, когда говорят вслух, это также кажется сумасшедшим. Это не так, но это требует как минимум объяснения и возможно своего рода когнитивного / синтаксического облегчения.
Вот краткая версия того, как это понять:
$var
имеет смысл в качестве имени параметра признака, поскольку он привязан к 1018 * представлению компилятора для переменной.
.var
необходим для доступа к user-eye представлению переменной с учетом представления компилятора.
Если переменная Scalar
, то необходим *1033*, а также , чтобы получить переменную, а не значение, которое она содержит. (Это не вредит, если это не Scalar
.)
Некоторое облегчение?
Я объясню выше более подробно через месяц, но сначала, как насчет облегчения?
Возможно, мы могли бы ввести новый Variable
метод, который делает .var.VAR
. Но, на мой взгляд, это будет ошибкой, если название метода не будет настолько хорошим, что оно по существу устраняет необходимость объяснения заклинания $var.var.VAR
, которое следует в следующем разделе этого ответа.
Но я сомневаюсь, что такое имя существует. Каждое имя, которое я придумаю, ухудшает положение. И даже если бы мы придумали идеальное имя, оно все равно едва ли стоило бы его в лучшем случае.
Я был поражен сложностью вашего исходного примера. Есть черта is
, которая вызывает черту does
. Поэтому, возможно, есть вызов для подпрограммы, которая абстрагирует и эту сложность, и $var.var.VAR
Но в любом случае существуют способы уменьшить эту двойную сложность, например:
role doc[$doc] { has $.doc is rw = $doc}
my $dog does doc['barks'];
say $dog.doc; # barks
Более длинное объяснение $var.var.VAR
Но $v
уже является переменной. Почему так много var
и VAR
с?
Действительно. $v
привязан к экземпляру класса Variable
. Разве этого не достаточно?
Нет, потому что Variable
:
Для хранения метаданных о переменной во время ее компиляции . (Возможно, его следовало бы назвать Metadata-About-A-Variable-Being-Compiled
? Просто шучу. Variable
выглядит хорошо в сигнатурах черт, и изменение его имени не помешало бы нам использовать и объяснить идиому $var.var.VAR
в любом случае.)
Разве мы не ищем дроида? Мы хотим пользовательский взгляд переменной. Тот, который был объявлен и скомпилирован, а затем используется как часть пользовательского кода. (Например, $dog
в строке say $dog...
. Даже если бы оно было BEGIN say $dog...
, поэтому оно выполнялось во время компиляции, $dog
будет все же относится к символу, который привязан к пользователю -eye view-контейнер или значение. Он не будет ссылаться на экземпляр Variable
, который является только представлением глаза компилятора данных , связанных с переменной.)
Упрощает жизнь компилятору и тем, кто пишет черты. Но это требует, чтобы писатель черт получил доступ к представлению глаза пользователя о переменной, чтобы получить доступ или изменить представление зрения пользователя. Атрибут .var
в Variable
хранит взгляд этого пользователя. (Я отмечаю, что у roast теста есть атрибут .container
, который вы пропустили. Теперь он явно переименован в .var
. Я предполагаю, что это потому, что переменная может быть связана с неизменяемым значением, а не с контейнером, поэтому имя .container
было сочтено вводящим в заблуждение.)
Итак, как нам прийти к $var.var.VAR
?
Давайте начнем с варианта вашего исходного кода, а затем двинемся вперед. Я переключусь с $dog
на @dog
и уроню .VAR
со строки say
:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a does role { has $.doc = $docced }
}
my @dog is docced('barks');
say @dog.doc; # No such method 'doc' for invocant of type 'Array'
Это почти работает. Одно крошечное изменение, и оно работает:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a.var does role { has $.doc = $docced }
}
my @dog is docced('barks');
say @dog.doc; # barks
Все, что я сделал, это добавил .var
в строку ... does role ...
. В вашем оригинале эта строка изменяет вид переменной с точки зрения компилятора, то есть объект Variable
, связанный с $a
. Он не изменяет вид переменной пользователем, то есть Array
, связанный с @dog
.
Насколько я знаю, теперь все работает правильно для множественных контейнеров, таких как массивы и хэши:
@dog[1] = 42;
say @dog; # [(Any) 42]
say @dog.doc; # barks
Но когда мы попробуем это с переменной Scalar
:
my $dog is docced('barks');
получаем:
Cannot use 'does' operator on a type object Any.
Это потому, что .var
возвращает то, что обычно возвращает переменная вида «глаз пользователя» С Array
вы получите Array
. Но с Scalar
вы получите значение, которое содержит Scalar
. (Это фундаментальный аспект P6. Он прекрасно работает, но вы должны знать это в подобных сценариях.)
Таким образом, чтобы это появилось для работы снова, мы должны также добавить пару .VAR
. Для чего-либо, кроме Scalar
a .VAR
- это «нет операции», так что это не повредит другим случаям, кроме Scalar
, чтобы добавить его:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a.var.VAR does role { has $.doc = $docced }
}
И теперь, похоже, работает Scalar
:
my $dog is docced('barks');
say $dog.VAR.doc; # barks
(Мне пришлось заново ввести .VAR
в строке say
по той же причине, по которой мне пришлось добавить его в строку $a.var.VAR ...
.)
Если бы все было хорошо, это был бы конец этого ответа.
Ошибка
Но что-то сломано. Если бы мы попытались инициализировать переменную Scalar
:
my $dog is docced('barks') = 42;
мы бы увидели:
Cannot assign to an immutable value
Как заметил @guifa, и Я наткнулся на некоторое время назад :
Кажется, что Scalar
с миксином больше не функционирует как контейнер, и назначение не выполняется. В настоящее время это выглядит для меня как ошибка.