Смешивание ролей в чертах, по-видимому, не работает - PullRequest
5 голосов
/ 01 мая 2019

Этот пример взят из roast, хотя он там уже 8 лет:

role doc { has $.doc is rw }

multi trait_mod:<is>(Variable $a, :$docced!) {
    $a does doc.new(doc => $docced);
}

my $dog is docced('barks');
say $dog.VAR;

Возвращает Any, без какой-либо роли. Очевидно, нет никакого способа добраться до части "doc", хотя эта черта не является ошибкой. Есть идеи?

Ответы [ 2 ]

5 голосов
/ 03 мая 2019

(Этот ответ основан на ответе @ 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 с миксином больше не функционирует как контейнер, и назначение не выполняется. В настоящее время это выглядит для меня как ошибка.

3 голосов
/ 03 мая 2019

Неудовлетворительный ответ, но, возможно, вы сможете добиться от него прогресса

role doc { 
  has $.doc is rw;
}

multi trait_mod:<is>(Variable:D $v, :$docced!) {
  $v.var.VAR does doc;
  $v.var.VAR.doc = $docced;
}

say $dog;            # ↪︎ Scalar+{doc}.new(doc => "barks")
say $dog.doc;        # ↪︎ barks
$dog.doc = 'woofs';  #
say $dog;            # ↪︎ Scalar+{doc}.new(doc => "woofs")

К сожалению, с этим что-то не так, и применение черты, кажется, приводит к тому, что переменная становится неизменной.

...