I wonder howto access an object's attribute dynamically via a name as a Str
at runtime in Raku
. Instead of:
#!/usr/bin/rakudo
class c0 {
has $!a0 = 1;
has $!a1 = 2;
method access(Str $m) {
if ($m eq "a0") { return $!a0; }
if ($m eq "a1") { return $!a1; }
}
method set(Str $m, $v) {
if ($m eq "a0") { $!a0 = $v; }
if ($m eq "a1") { $!a1 = $v; }
}
}
my $c = c0.new();
$c.set("a0", 3);
$c.set("a1", 4);
say $c.access("a0");
say $c.access("a1");
I would like to use something that would look in pseudocode:
class c0 {
...
method access(Str $m) {
return self.$m;
}
method set(Str $m, $v) {
self.$m = $v;
}
}
Is this possible in Raku
? Which construct do I need to use?
As a backgrounder I was thinking how to implement a role that adds associativity functionality to the class, to transparently access a member. The attribute name would be parametrized: If I have a class class ports { has @!ports_; ... }
and an instance my $p = ports.new()
then I want to be able to use the subscript syntax to access @ports_ via $p[...] . I try to figure out weather I can define role acc [ Str $member] does Associative[Cool,Str] { ... }
and then define ports via class ports does acc["ports_"] { ... }
where the AT-KEY
and EXISTS-KEY
in role acc
are implemented using dynamic attribute access (if that is possible).
I dont want to use "EVAL".
One of the outstanding features of ABAP is the capability of dynamic programming. In particular, you can use What about dynamic ABAP Objects? Well, apart from program generation all dynamic features listed above are readily available in ABAP Objects too. Especially, you can use: • Dynamic attribute access via field symbols:
To access the object property dynamically in JS, you need to pass the property name as a string in square brackets. As the value of the variable key changed, we got access to the different properties of the user object. The most common way to access the object properties in JavaScript is the dot.
CALL METHOD oref- > (‘METH’). Dynamic subroutine pool with a local class. The dynamic class implements a global interface that is used to type the reference variable oref. Instead of an interface you could also inherit from a global super class to achieve static typing of the reference variable.
Remains only the question how to access the local classes or interfaces of a dynamic subroutine pool. One feasable way would be to create exactly one subroutine (as a factory) in the subroutine pool that wraps the access to ABAP Objects. Another way includes ingredient 2. Then, there is no need for subroutines at all.
This is possible with some introspection of the attributes. However, I would like to point out that it is the exact intention of private attributes to be private. Creating a workaround to handle them as public attributes is an anti-pattern, and introduces needless complexity.
class c0 {
has $.a0 = 1;
has $.a1 = 2;
method access (Str $m) {
my $attribute = self.^attributes.first({ ~$_ eq '$!' ~ $m });
return unless $attribute;
$attribute.get_value(self); # 1
}
}
my $c = c0.new;
say $c.access('a0');
For setting the value, you can use the .set_value
method on the attribute.
method set (Str $m, $v) {
...
$attribute.set_value(self, $v);
}
Old answer left here for historic purposes.
Yes, something like this is possible in Raku. You don't even need to explicitly define the access
method.
class c0 {
has $.a0 = 1;
has $a.1 = 2;
}
my $c = $c0.new;
say $c.'a0'(); # 1
This works because Raku creates an accessor method for public variables for your classes, which is called when you use .'a0'()
. The ()
are required for using a quoted method name.
You changed your post to add a question about how to do something like this:
role acc [ Str $member] does Associative[Cool,Str] { ... }
class ports does acc["ports_"] { has @!ports_; ... }
The answer is of course, don't do that.
I mean you can, but you really shouldn't.
I mean you really really shouldn't.
Also you indicate that you want to use []
for indexing.
The thing is that is Positional
not Associative
.
(I'm ignoring the fact that there is no point to add _
to the end of the attribute name. Usually in Perl or Python adding _
indicated private
, but we don't need to do that in Raku.)
The right way to do that is to have the array inside of the role.
role Array::Access [::OF = Cool] does Positional[OF] {
has OF @!array-access handles < AT-POS >;
}
class Ports does Array::Access {
# allows you to access it as self!ports inside of this class
method !ports () is raw { @!array-access }
}
Which shows that adding a role to do that is probably overkill.
class Ports does Positional[Cool] {
has Cool @!ports handles < AT-POS >;
}
If you really, really want to do it they way you asked for, the following works.
role Inner::Array::Access [ Str:D \name, ::OF = Cool ] does Positional[OF] {
# a way to quickly access the attribute
# (hopefully no-one tries to add an attribute of this name to their class)
has $!inner-array handles < AT-POS >;
# set $!inner-array
submethod TWEAK (){
$!inner-array := self.^attributes.first(name).get_value(self);
}
}
class Ports does Inner::Array::Access['@!ports'] {
has @!ports;
# a quick way to add a way to set @!ports for our test
submethod BUILD( :@!ports ){}
}
my Ports $v = ports => [0,10,20,30];
say $v[2]; # 20
Probably what you were thinking is embed the self.^attributes
thing into AT-POS
.
role Inner::Array::Access [ Str:D \name, ::OF = Cool ] does Positional[OF] {
method AT-POS ( \index ) is raw {
self.^attributes.first(name).get_value(self).AT-POS(index);
}
}
That would be slow, because it has to do all of those lookups everytime you access a single element.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With