Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply a proxy to a variable (not an attribute) using traits

Tags:

raku

rakudo

This question is a near-duplicate of Apply a proxy using traits. However, that question dealt with applying a proxy to an Attribute, and I would like to do the same thing for a Variable. From Jonathan's answer, I understand that I

need to arrange for the Proxy to be bound into the attribute, so that there's a Proxy there rather than a Scalar container that is usually created by class initialization logic.

However, I can't seem to bind successfully to a Variable:D, even at compile time. (Including with nqp::bind). I'd greatly appreciate any pointers in the correct direction.

(Ideally, I'd like to support using the variable/trait with assignment syntax. In a perfect world, I'd have syntax like:

my $thing is custom-proxy = 42;

And the result of that would be that $thing is containerized inside the Proxy, but not in a Scalar. But if that's not possible, I'd settle for getting it working with binding via :=.

[EDIT: building on the accepted answer below, it is possible to mostly do this with the following code:


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))
}

This works for variables that are not initialized to a different value or for state variables on calls to the function other than the first.

like image 873
codesections Avatar asked May 06 '21 05:05

codesections


Video Answer


1 Answers

You can always bind.

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;

Which currently results in the following.

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

(The repeated FETCH calls are a known limitation.)


If you wanted to have syntax like

my $thing is custom-proxy = 42;

You would need to start with

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

The problem is that currently doing it this way requires a lot of deep Rakudo/nqp knowledge that I do not possess.

For example the code behind my $var is default('value') looks a bit like this:

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);
}

Why does that have $what.^mixin_base?
I have no idea.

Why isn't $!descriptor accessible something like $v.var.descriptor?
I have no idea.

How do we change $v.var.VAR from a Scalar to a Proxy?
I have no idea.

Is that last one doable? (From within a trait_mod:<is>) I am fairly certain that the answer is yes.

like image 119
Brad Gilbert Avatar answered Nov 11 '22 11:11

Brad Gilbert