How to access attributes in object dynamically in Raku - object

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".

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.

Related

Is it possible in Mono.Cecil to determine the actual type of an object on which a method is called?

For example, consider the following C# code:
interface IBase { void f(int); }
interface IDerived : IBase { /* inherits f from IBase */ }
...
void SomeFunction()
{
IDerived o = ...;
o.f(5);
}
I know how to get a MethodDefinition object corresponding to SomeFunction.
I can then loop through MethodDefinition.Instructions:
var methodDef = GetMethodDefinitionOfSomeFunction();
foreach (var instruction in methodDef.Body.Instructions)
{
switch (instruction.Operand)
{
case MethodReference mr:
...
break;
}
yield return memberRef;
}
And this way I can find out that the method SomeFunction calls the function IBase.f
Now I would like to know the declared type of the object on which the function f is called, i.e. the declared type of o.
Inspecting mr.DeclaringType does not help, because it returns IBase.
This is what I have so far:
TypeReference typeRef = null;
if (instruction.OpCode == OpCodes.Callvirt)
{
// Identify the type of the object on which the call is being made.
var objInstruction = instruction;
if (instruction.Previous.OpCode == OpCodes.Tail)
{
objInstruction = instruction.Previous;
}
for (int i = mr.Parameters.Count; i >= 0; --i)
{
objInstruction = objInstruction.Previous;
}
if (objInstruction.OpCode == OpCodes.Ldloc_0 ||
objInstruction.OpCode == OpCodes.Ldloc_1 ||
objInstruction.OpCode == OpCodes.Ldloc_2 ||
objInstruction.OpCode == OpCodes.Ldloc_3)
{
var localIndex = objInstruction.OpCode.Op2 - OpCodes.Ldloc_0.Op2;
typeRef = locals[localIndex].VariableType;
}
else
{
switch (objInstruction.Operand)
{
case FieldDefinition fd:
typeRef = fd.DeclaringType;
break;
case VariableDefinition vd:
typeRef = vd.VariableType;
break;
}
}
}
where locals is methodDef.Body.Variables
But this is, of course, not enough, because the arguments to a function can be calls to other functions, like in f(g("hello")). It looks like the case above where I inspect previous instructions must repeat the actions of the virtual machine when it actually executes the code. I do not execute it, of course, but I need to recognize function calls and replace them and their arguments with their respective returns (even if placeholders). It looks like a major pain.
Is there a simpler way? Maybe there is something built-in already?
I am not aware of an easy way to achieve this.
The "easiest" way I can think of is to walk the stack and find where the reference used as the target of the call is pushed.
Basically, starting from the call instruction go back one instruction at a time taking into account how each one affects the stack; this way you can find the exact instruction that pushes the reference used as the target of the call (a long time ago I wrote something like that; you can use the code at https://github.com/lytico/db4o/blob/master/db4o.net/Db4oTool/Db4oTool/Core/StackAnalyzer.cs as inspiration).
You'll need also to consider scenarios in which the pushed reference is produced through a method/property; for example, SomeFunction().f(5). In this case you may need to evaluate that method to find out the actual type returned.
Keep in mind that you'll need to handle a lot of different cases; for example, imagine the code bellow:
class Utils
{
public static T Instantiate<T>() where T : new() => new T();
}
class SomeType
{
public void F(int i) {}
}
class Usage
{
static void Main()
{
var o = Utils.Instantiate<SomeType>();
o.F(1);
}
}
while walking the stack you'll find that o is the target of the method call; then you'll evaluate Instantiate<T>() method and will find that it returns new T() and knowing that T is SomeType in this case, that is the type you're looking for.
So the answer of Vagaus helped me come up with a working implementation.
I published it on github - https://github.com/MarkKharitonov/MonoCecilExtensions
Included many unit tests, but I am sure I missed some cases.

Stan: Local scopes in functions

I am fairly new to Stan and have gone through the manual (version 2.23). What was new to me was that variable hiding is not allowed: you can not use a local variable (e.g. in a for-loop) that has been defined globally (i.e. outside the for-loop) (chapter 7.9, Local Variable Declarations).
Is the same true for user-defined functions? I.e., can you declare variables in user-defined functions that have the same name as other variables, that have been declared outside the function? In our case, we have
functions{
real[] my_function (x) {
real init[K*2] = some_declaration_involving_x
return(some_other_value_involving_init[])
}
}
transformed data {
real init[K*6] = some_other_declaration; // initial values
}
transformed parameters {
yet_another_variable = my_function(some_variable)
}
Yes, it's easy enough to test:
functions {
int fun() {
int N = 1;
return N;
}
}
model {
real N = 2;
print(fun(), N);
}

Unhandled Exception: type 'String' is not a subtype of type 'List<String>' of 'value'

I am completely baffled. I am essentially trying to do foo["x"]="y" and getting the most baffling of exceptions. I actually made a minimal working example, and I had to nest Maps and Lists for the thing to break. I have no idea what's going on.
Map fixprobset(Map P) {
print(P);
if(!P.containsKey("name")) {
final foo = P["tileset"].join(" ");
P["name"] = foo;
}
if(P.containsKey("children")) {
for(var k=0; k<P["children"].length;k++) fixprobset(P["children"][k]);
}
return P;
}
void main() {
Map problemset = fixprobset({
"name": "Jingle Jangle",
"children": [
{
"tileset": ["1","2","3"]
}
]
});
print(problemset);
}
Click here and gaze in wonder on the bafflement
OK so now I understand what's going on. Objects have a runtimeType, and even though P has the unassuming type declaration Map, P.runtimeType could be e.g. Map<String,List<String>>. Once that happens, a Map can no longer store heterogeneous objects. Getting around this was quite hard for me, the type declarations Map<String,dynamic> P or Map<String,Object> P really don't help, the undesirable runtimeType is there to stay. I've filed this as a "bug" although perhaps it will be deemed a (mis)feature.
This gist has the workaround.
You problems comes from the fact that your are fighting against the type system in Dart. As you have discovered, there are something about the runtimeType which seems to be automatically assigned to some other type than dynamic.
I have made this example which works:
Map fixprobset(Map<dynamic, dynamic> P) {
print(P);
if (!P.containsKey("name")) {
final foo = (P["tileset"] as List).join(" ");
P["name"] = foo;
}
if (P.containsKey("children")) {
for (var k = 0; k < (P["children"] as List).length; k++) {
fixprobset((P["children"] as List)[k] as Map);
}
}
return P;
}
void main() {
final problemset = fixprobset(<dynamic, dynamic>{
"name": "Jingle Jangle",
"children": <dynamic>[
<dynamic, dynamic>{
"tileset": <dynamic>["1", "2", "3"]
}
]
});
print(problemset);
}
As you can see, the automatically assigned types comes from your input data which gets assigned types which makes sense at the start of the program. Your problem is then, that you don't wants this automatically assigned types but want to change them after creation. So you want to change a List<String> into a List<dynamic> but to do that, you need to create a new object since you cannot change the type of an object after its creation.
So my "solution" fixes this by setting the type at the time of the creation of the lists and maps in the input of the first call to fixprobset.

Closure with conditional logging

I have a function like this:
private downloadAllFiles() {
sftpRetriever.listFiles().findAll {
filter.isResponse(it) || filter.isResponseTurned(it)
}.each { String fileName ->
log.info 'Downloading file: {}', fileName
sftpRetriever.downloadFile(fileName)
log.info 'File downloaded'
removeRemoteFile(fileName)
}
}
I am looking for a simple way of modyfing this closure inside of that function so if the size() of findAll is 0 it will simply log 'No more files to download' and .each won't be executed. Is there any simple way to make it in single closure? It is really simply task if I divide it in several parts, but trying to learn closures here and improve my expressiveness :) Thank you in advance for your help.
Take a look at creature below :) It works due the fact that each returns the collection on which it's invoked (+ elvis operator and quite nice Groovy's truth evaluation):
def printContents(Collection collection) {
collection.each {
println it
} ?: println('Collection is empty')
}
printContents([1,2,'hello'])
printContents([])
I don't like this syntax but it's the shorter version which came to my mind.
You can also use metaprogramming to add the method provided by Steinar. It must be added to metaClass before first use but you'll avoid an effort to make extension module:
Collection.metaClass.doIfEmpty { Closure ifEmptyClosure ->
if (delegate.empty) {
ifEmptyClosure()
}
return delegate
}
def printContents(Collection collection) {
collection.doIfEmpty {
println "Collection is empty"
}.each {
println it
}
}
printContents([1,2,'hello'])
printContents([])
One rather generic and reusable option is to extend Collection using an extension module. This is surprisingly easy to do and is even recognized in IDE's (at least in IntelliJ) so you get code completion, etc.
For example, write an the extension class for collections which will perform the closure if the collection is empty. In addtion, it should always return the collection to allow further chaining:
package stackoverflow
class CollectionExtension {
static <T extends Collection> T doIfEmpty(T self, Closure closure) {
if (self.empty) {
closure()
}
return self
}
}
You will also need to tell groovy that this file is an extension module. Add a property file as a resource on the classpath: META-INF/services/org.codehaus.groovy.runtime.ExtensionModule (note: this name and location is mandatory for extension modules, i.e. you cannot change it).
moduleName=stackoverflow-module
moduleVersion=1.0
extensionClasses=stackoverflow.CollectionExtension
Finally a simple test script to show how this can be used:
def printContents(Collection collection) {
collection.doIfEmpty {
println "Collection is empty"
}.each {
println it
}
}
printContents([1,2,'hello'])
printContents([])
Output:
1
2
hello
Collection is empty
You may try the following piece of code:
def l1 = [1,2,3,4]
def l2 = [5,6,7,8]
def m(list) {
list.findAll { it < 5}.with { l ->
size > 0 ?
l.each { e ->
println e
}
:
println('Zero elements found')
}
}
m(l1)
m(l2)
No better idea at the moment.

Dynamically added groovy property is not unique per-instance

If I dynamically add a property to a class, each instance of the class is initialized with a reference to the same value (even though the properties are correctly at different addresses, I don't want them to share the same reference value):
Here's an example:
class SolarSystem {
Planets planets = new Planets()
static main(args) {
SolarSystem.metaClass.dynamicPlanets = new Planets()
// Infinite loop
// SolarSystem.metaClass.getDynamicPlanets = {
// if (!delegate.dynamicPlanets.initialized) {
// delegate.dynamicPlanets = new Planets(initialized: true)
// }
//
// delegate.dynamicPlanets
// }
// No such field: dynamicPlanets for class: my.SolarSystem
// SolarSystem.metaClass.getDynamicPlanets = {
// if (!delegate.#dynamicPlanets.initialized) {
// delegate.#dynamicPlanets = new Planets(initialized: true)
// }
//
// delegate.#dynamicPlanets
// }
SolarSystem.metaClass.getUniqueDynamicPlanets = {
if (!delegate.dynamicPlanets.initialized) {
delegate.dynamicPlanets = new Planets(initialized: true)
}
delegate.dynamicPlanets
}
// SolarSystem.metaClass.getDynamicPlanets = {
// throw new RuntimeException("direct access not allowed")
// }
def solarSystem1 = new SolarSystem()
println "a ${solarSystem1.planets}"
println "b ${solarSystem1.dynamicPlanets}"
println "c ${solarSystem1.uniqueDynamicPlanets}"
println "d ${solarSystem1.dynamicPlanets}"
println ''
def solarSystem2= new SolarSystem()
println "a ${solarSystem2.planets}"
println "b ${solarSystem2.dynamicPlanets}"
println "c ${solarSystem2.uniqueDynamicPlanets}"
println "d ${solarSystem2.dynamicPlanets}"
}
}
In a separate file:
class Planets {
boolean initialized = false
}
When this runs, you see something like this:
a my.Planets#4979935d
b my.Planets#66100363
c my.Planets#5e0feb48
d my.Planets#5e0feb48
a my.Planets#671ff436
b my.Planets#66100363
c my.Planets#651dba45
d my.Planets#651dba45
Notice how for solarSystem2, the 'normal' member variable planets has a different address when the two objects are created. However, the dynamically added dynamicPlanets points to the same object that solarSystem1 pointed to (in this case, at address 66100363).
I can reassign them in my dynamic getter (getUniqueDynamicPlanets), and that fixes the problem.
However, I cannot override the getDynamicPlanets getter, because I either get an infinite loop, or I cannot get direct access to the dynamically-added property.
Is there a way to directly access the dynamically-added property so I could handle this in the getDynamicPlanets getter? Is there a better strategy for this altogether? Sorry if I missed it, I've looked a bunch...
Thanks
I'm not 100% sure I understand your question, but if I do, did you try setting the getDynamicPlanets closure to have explicitly 0 parameters, so:
SolarSystem.metaClass.getDynamicPlanets = {-> ... }
If you don't have the -> with no args before it, there is an implicit it parameter that's assigned and it's not a zero arg method, so doesn't adhere to the javabean getter/setter pattern.

Resources