Как узнать, возвращает ли l-значение при использовании `FALLBACK`? - PullRequest
5 голосов
/ 24 сентября 2019

Как узнать, нужно ли мне возвращать значение l при использовании FALLBACK?

Я использую return-rw, но я бы хотел использовать return только там, где это возможно.Я хочу отслеживать, действительно ли я изменил %!attrs или только что прочитал значение при вызове FALLBACK.

Или (альтернативный план B) я могу прикрепить обратный вызов или что-то похожее на мой %!attrs следить за изменениями?

class Foo {
  has %.attrs;
  submethod BUILD { %!attrs{'bar'} = 'bar' }

#  multi method FALLBACK(Str:D $name, *@rest) {
#    say 'read-only';
#    return %!attrs{$name} if %!attrs«$name»:exists;
#  }

  multi method FALLBACK(Str:D $name, *@rest) {
    say 'read-write';
    return-rw %!attrs{$name} if %!attrs«$name»:exists;
  }
}

my $foo = Foo.new;
say $foo.bar;

$foo.bar = 'baz';
say $foo.bar;

Ответы [ 2 ]

6 голосов
/ 24 сентября 2019

Это похоже на вопрос XY, поэтому давайте упростим пример и посмотрим, поможет ли этот ответ в ваших решениях.

Прежде всего: если вы возвращаете «значение» несуществующеговведите хеш, вы на самом деле возвращаете контейнер, который автоматически оживит ключ в хеш-коде при назначении:

my %hash;
sub get($key) { return-rw %hash{$key} }
get("foo") = 42;
dd %hash;   # Hash %hash = {:foo(42)}

Обратите внимание, что вам необходимо использовать return-rw здесь, чтобы убедиться, чтоконтейнер возвращается, а не только значение в контейнере.Кроме того, вы можете использовать черту is raw, которая позволяет вам просто установить последнее значение:

my %hash;
sub get($key) is raw { %hash{$key} }
get("foo") = 42;
dd %hash;   # Hash %hash = {:foo(42)}

Обратите внимание, что вы должны не использовать return в этом случае, так какэто все равно будет снова де-контейнеризировать.

Чтобы вернуться к вашему вопросу:

Я хочу отслеживать, действительно ли я изменил %!attrs или только что прочитал значениепри вызове FALLBACK.

class Foo {
    has %!attrs;
    has %!unexpected;

    method TWEAK() { %!attrs<bar> = 'bar' }

    method FALLBACK(Str:D $name, *@rest) is raw {
        if %!attrs{$name}:exists {
            %!attrs{$name}
        }
        else {
            %!unexpected{$name}++;
            Any
        }
    }
}

Это либо вернет контейнер, найденный в хэше, либо запишет доступ к неизвестному ключу и вернет неизменный Any.

Относительно плана B, запись изменений: для этого вы можете использовать для этого объект Proxy .

Надеюсь, это поможет в вашем квесте.

2 голосов
/ 25 сентября 2019

Ответ Лиз полон полезной информации, и вы приняли ее, но я подумал, что следующее все еще может представлять интерес.

Как узнать, возвращает ли l-значение ...?

Давайте начнем с игнорирования предложения FALLBACK.

Вам нужно будет проверить значение.Чтобы справиться с Scalar s, вы должны проверить значение .VAR.(Для не Scalar значений .VAR действует как "без операции".) Я думаю (но не цитируйте меня), что Scalar|Array|Hash охватывает все значения l супертипы:

my \value       = 42;  # Int is an l-value is False
my \l-value-one = $;   # Scalar is an l-value is True
my \l-value-too = @;   # Array is an l-value is True

say "{.VAR.^name} is an l-value is {.VAR ~~ Scalar|Array|Hash}"
  for value, l-value-one, l-value-too

Как узнать, возвращает ли значение l при использовании FALLBACK?

Добавление«при использовании FALLBACK» не имеет значения для ответа.

Как узнать, действительно ли мне нужно , чтобы вернуть l-значение ...?

Опять давайте начнем с игнорирования предложения FALLBACK.

Это совершенно другой вопрос, чем "Как узнать, возвращает ли l-значение ..."?».Я думаю, что это суть вашего вопроса.

Афаик, ответ таков: вам нужно предвидеть, как будет использоваться возвращаемое значение.Если есть любой шанс, что он будет использоваться как l-значение , и вы хотите, чтобы это использование работало, тогда вам нужно вернуть l-значение ,Язык / компилятор не может (или, по крайней мере, не помогает) принять это решение.

Рассмотрим некоторые связанные сценарии:

my $baz := foo.bar;
... (100s of lines of code) ...
$baz = 42;

Если только первая строка не возвращает l-значение , вторая строка потерпит неудачу.

Но ситуация на самом деле гораздо более непосредственная, чем эта:

routine-foo = 42;

routine-foo оценивается первым, полностью, до вычисления выражения lhs = rhs .

Если только разрешение компилятора вызова routine-foo не каким-то образом учитывает тот факт, что в ближайшее время произойдет то, что lhs будетЕсли он будет назначен, то для однократно или многократно отправленного routine-foo не будет возможности узнать, может ли он безопасно вернуть r-значение или должен возвращать l-значение .

И разрешение компилятора не включает это.Так, например:

multi term:<bar> is rw { ... }
multi term:<bar>       { ... }
bar = 99; # Ambiguous call to 'term:<bar>(...)'

Я могу представить этот один день (через N лет), который решается комбинацией, позволяющей = быть перегружаемым оператором, надежными макросамикоторые допускают перегрузку = и возможность изменения стандартного разрешения, чтобы вышеуказанный неоднозначный вызов мог сделать что-то эквивалентное разрешению is rw multi.Но я сомневаюсь, что на самом деле сбудется даже при N = 10.Возможно, есть другой способ, но я не могу придумать один в данный момент.

Как я могу узнать, действительно ли мне нужно возвращать l-значение , когдаиспользование FALLBACK?

Снова добавление слова "при использовании FALLBACK" не имеет значения для ответа.

Я хочу отслеживать, если я 'мы действительно изменили %!attrs или только что прочитали значение при вызове FALLBACK.

Когда вызывается FALLBACK, он не знает, в каком контексте он вызывается - r-значение или l-значение .Любая модификация происходит после того, как она уже вернулась.

Другими словами, любое решение, которое вы придумали, само по себе не имеет ничего общего с FALLBACK (даже если вам придется использовать его для реализации какого-либо другого аспектачто бы вы ни пытались сделать).

(Даже если бы это было так, я подозреваю, что попытка решить это с помощью FALLBACK просто сделает дела хуже .два FALLBACK multis, один с чертой is rw, но, как объяснено выше, мое воображение не расширяется до того, чтобы что-то изменить в ближайшее время, если вообще когда-либо, и могло произойти, только если вышеперечисленные воображаемые вещи произошли (макросы и т. д.) и компилятор был также модифицирован, чтобы обратить внимание на два FALLBACK мульти-варианта, и я вовсе не хочу утверждать, что это даже имеет смысл.)

План Б

Или (альтернативный план B) я могу прикрепить обратный вызов или что-то похожее на мой %!attrs для отслеживания изменений?

Как отмечает Лизмат, это область Proxy с.И, таким образом, ваш следующий ТАК вопрос ...:)

...