Применить прокси к переменной (не атрибуту) с помощью трейтов

Этот вопрос практически повторяет Применение прокси с использованием признаков. Однако этот вопрос касается применения прокси к атрибуту, и я хотел бы сделать то же самое для переменной. Из ответа Джонатана я понимаю, что я

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

Однако я не могу успешно выполнить привязку к переменной: D даже во время компиляции. (В том числе с nqp :: bind). Буду очень признателен за любые указатели в правильном направлении.

(В идеале я хотел бы поддерживать использование переменной / признака с синтаксисом присваивания. В идеальном мире у меня был бы синтаксис вроде:

my $thing is custom-proxy = 42;

И результатом этого будет то, что $thing будет помещен в контейнер внутри прокси, но не в скаляре. Но если это невозможно, я бы согласился заставить его работать с привязкой через :=.

[ИЗМЕНИТЬ: основываясь на принятом ниже ответе, это можно в основном сделать с помощью следующего кода:


multi trait_mod:<is>(Variable \v, :$tom) {
    v.block.add_phaser(
        'ENTER',
        v.willdo(<-> $_ {
            $_ = Proxy.new:
                     STORE => -> $, $v { say "store $v" },
                     FETCH => { say "fetch!"; 42}
                }, 1))
}

Это работает для переменных, которые не инициализированы другим значением, или для state переменных при вызовах функции, отличной от первой.


person codesections    schedule 06.05.2021    source источник


Ответы (2)


Мой 2d [1]:

Я бы согласился заставить его работать с привязкой через :=.

sub custom-proxy is rw { Proxy.new: FETCH => { 42 }, STORE => { ... } }
my $variable := custom-proxy;
say $variable; # 42

В идеальном мире у меня был бы синтаксис вроде:

my $thing is custom-proxy = 42;

Айуи, это намерение @Larry.

Но, как вы, вероятно, знаете, если тип (например, role custom-proxy { ... }) применяется с использованием признака is к скалярной переменной (например, my $variable is custom-proxy), тогда компилятор выдает сообщение об ошибке времени компиляции (is trait on $-sigil variable not yet implemented).

Кажется, я не могу успешно выполнить привязку к Variable:D даже во время компиляции

Во-первых, давайте выясним, что такое Variable и к чему вам нужно будет успешно выполнить привязку:

multi trait_mod:<is>(Variable \var, :$foo!) { say var.var.VAR.WHAT } # (Scalar)
my $variable is foo;

Вы могли подумать, что можете привязать к var. Но компилятор передает lvalue, поэтому вы не сможете его изменить.

Вы могли подумать, что можете привязать к var.var, который является атрибутом Variable. (Я объясняю, что такое Variable и его атрибут var, и почему мне пришлось написать varvarVAR! В приведенном выше коде, здесь.)

Связанный вами SO показывает, как изменить значение, привязанное к атрибуту в некотором объекте:

$a.set_build: -> \SELF, | {
  $a.set_value: SELF, Proxy.new:
    STORE => -> $, $val { say "store $val" },
    FETCH => { say "fetch!"; 42 }
}

Так, возможно, вы могли бы использовать этот подход для изменения атрибута .var в Variable?

К сожалению, настройка логики сборки используется для привязки атрибута ... при каждом создании объекта (следовательно, вы переопределите любое начальное значение по умолчанию).

Так что я не думаю, что этот метод поможет в этом случае, потому что Variable и, следовательно, его атрибут .var, по-видимому, уже были созданы к тому времени, когда Variable передается в черту is.

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


Я предполагаю, что изменение Raku (do) так, чтобы атрибут Variable .var стал доступным для записи, или использование метапрограммирования для погружения под общедоступный API Variable для принудительного внесения изменений было бы слишком чревато, неоправданно усложняя код обработки переменных компилятора и / или заменяя его логика оптимизации кодогенератора для логики пессимизации.

Это может быть причиной предположений @Larry о том, что когда-нибудь будет реализована более контролируемая is type на скалярных переменных.

Сноски

[1] Мои два (пенни | dogecoin).

person raiph    schedule 06.05.2021

Всегда можно связать.

my $actual-thing = 42;

my $thing := Proxy.new(
    FETCH => anon method fetch () {
        say 'fetch';
        $actual-thing
    },
    STORE => anon method store ($new) {
        say 'store ',$new;
        $actual-thing = $new
    }
);

say $thing;
$thing = 5;
say $thing;

Что в настоящее время приводит к следующему.

fetch
fetch
fetch
fetch
fetch
fetch
fetch
42
store 5
fetch
fetch
fetch
fetch
fetch
fetch
fetch
5

(Повторяющиеся FETCH вызовы - известное ограничение.)


Если вам нужен синтаксис вроде

my $thing is custom-proxy = 42;

Вам нужно будет начать с

multi trait_mod:<is> ( Variable:D \var, :$custom-proxy! ){
    …
}

Проблема в том, что в настоящее время для этого требуется много глубоких знаний Rakudo / nqp, которых у меня нет.

Например, код my $var is default('value') выглядит примерно так:

multi sub trait_mod:<is>(Variable:D $v, Mu :$default!) {
    my $var  := $v.var;
    my $what := $var.VAR.WHAT;

    my $descriptor;
    {
        $descriptor := nqp::getattr($var, $what.^mixin_base, '$!descriptor');
        CATCH {
            my $native = $v.native($what);
            …
        }
    }
    …
    $descriptor.set_default(nqp::decont($default));

    # make sure we start with the default if a scalar
    $var = $default if nqp::istype($what, Scalar);
}

Почему там $what.^mixin_base?
Понятия не имею.

Почему $!descriptor недоступно что-то вроде $v.var.descriptor?
Понятия не имею.

Как нам изменить $v.var.VAR с Scalar на Proxy?
Понятия не имею.

Это последнее выполнимо? (Изнутри trait_mod:<is>) Я почти уверен, что ответ - да.

person Brad Gilbert    schedule 06.05.2021