I have a class with attributes. I want to check whether some but not all are defined. So:
class A {
has $.a is rw;
has $.b is rw;
has $.c is rw;
has $.d is rw;
method delete { ... }
}
my A $x .= new(:a<hi>, :d<good>);
## later
$x.b = 'there';
## code in which $x.c may or may not be defined.
## now I want to check if the attributes a, b, and c are defined, without
## needing to know about d
my Bool $taint = False;
for <a b c> {
$taint &&= $x.$_.defined
}
This will cause errors because an object of type A doesn't have a method 'CALL-ME' for type string.
Is there an introspection method that gives me the values of attributes of a class?
$x.^attributes gives me their names and types, but not their values.
I think there must be some way since dd or .perl provide attribute values - I think.
Yes, it is called get_value. It needs the object of the attribute passed to it. For example:
class A {
has $.a = 42;
has $.b = 666;
}
my $a = A.new;
for $a.^attributes -> $attr {
say "$attr.name(): $attr.get_value($a)"
}
# $!a: 42
# $!b: 666
Related
In raku it seems possible to define static methods (via sub keyword) and static attributes (via my) Those can be referenced inside the same class.
However, is it possible to access those methods and attributes outside of the class?
Something similar to this:
class MyClass {
my $attribute = 123;
sub my-method {
say 'Hello';
}
}
MyClass.$attribute;
MyClass.my-method;
it seems possible to define static methods (via sub keyword) and static attributes (via my) Those can be referenced inside the same class.
I can see why you're calling them static methods and attributes but Raku has a much simpler solution for those:
class MyClass {
method my-method {
say 'Hello';
}
method attribute is rw {
state $attribute = 123
}
}
say MyClass.attribute; # 123
MyClass.attribute = 99;
say MyClass.attribute; # 99
MyClass.my-method; # Hello
You could use our subs and our variables. our is the declarator used to define a lexical that is also for use outside the package it's declared withing. (mys are never shared; a sub declarator without an our is the same as my sub.)
So:
class MyClass {
our sub my-sub {
say 'Hello';
}
our $attribute = 123
}
import MyClass;
say $MyClass::attribute; # 123
$MyClass::attribute = 99;
say $MyClass::attribute; # 99
MyClass::my-sub; # Hello
As you can see, these aren't methods; this approach ignores OOP in the sense the prior solution does not.
I'm aware that Perl is not statically typed when I want to apply this mechanism to a Perl object of a derived class:
Say I have a base class B and a derived class D inheriting from B.
Also I have an object $obj that holds a D object.
A function Bf() is expecting a parameter of type B.
Obviously (by the rules of polymorphism) I can pass $obj to Bf() like Bf($obj), but unlike to a static-typed language Bf() will see the whole D object (and not just the elements of B).
Is there a (rather clean and simple) solution for this problem in Perl? The solution should "hide" the attributes (and methods) a B does not have from D in Bf(), not restricting modifications of the original B (which is D actually).
Adult Programmers only (added 2020-03-06)
OK, people wanted a more concrete description.
Unfortunately (as pointed out) the original program is highly complex and uses reflection-like mechanisms to generate getters, setters and formatters automatically, to I really can't give a minimum working example here, because it would not be minimal.
First I have a class MessageHandler that handle messages (no surprise!).
Then I have a function log_message($$$) that expects (among others) a MessageHandler object as first argument.
Then I have this hierarchy of classes (it's much more complex in reality):
MessageHandler
ControlMessageHandler (ISA: MessageHandler)
ControlMessageResponseHandler (ISA: ControlMessageHandler)
Now if log_message wants a MessageHandler I can pass a ControlMessageResponseHandler as it conforms to MessageHandler.
But doing so exposes all the attributes of ControlMessageResponseHandler to log_message that are non-existent in MessageHandler.
The danger is that log_message might (by mistake) access an attribute of ControlMessageResponseHandler that is not present in MessageHandler. To prevent errors I'd like to prevent that, or at least get some warning (like I would get in a statically-typed language as Eiffel).
Dirty Details inside
Just in case it matters, I'll sketch how my array objects are built (a lot of extra code would be needed for a working example):
First the array indices are allocated automatically like this:
use constant I_VERBOSITY => IS_NEXT->(); # verbosity level
use constant I_TAG => IS_NEXT->(); # additional tag
use constant I_TAG_STACK => IS_NEXT->(); # tag stack
use constant I_MSG_DEBUG => IS_NEXT->(); # handler for debug messages
...
use constant I_LAST => IS_LAST->(); # last index (must be last)
I_LAST is needed for inheritance.
The attributes are defines like this:
use constant ATTRIBUTES => (
['verbosity', I_VERBOSITY, undef],
['tag', I_TAG, \&Class::_format_string],
['tag_stack', I_TAG_STACK, undef],
['msg_debug', I_MSG_DEBUG, \&Class::_format_code],
...
);
The definition contains a hint how to format each attribute.
This information is used to set up formatters to format each attribute like this:
use constant FORMATTERS =>
(map { Class::_attribute_string($_->[0], $_->[1], undef, $_->[2]) }
ATTRIBUTES); # attribute formatters
Getters and setters are automatically defined like this:
BEGIN {
foreach (ATTRIBUTES) {
Class::_assign_gs_ai(__PACKAGE__, $_->[0], $_->[1]);
}
}
The constructor would use the following lines:
my $self = [];
$#$self = I_LAST;
$self->[I_VERBOSITY] = $verbosity;
...
And finally my object print routine goes like this:
sub as_string($)
{
my $self = shift;
my $a_sep = ', ';
return join($a_sep, map { $_->($self, $a_sep) } FORMATTERS);
}
With inheritance it looks like this:
sub as_string($)
{
my $self = shift;
my $a_sep = ', ';
return join($a_sep, $self->SUPER::as_string(),
map { $_->($self, $a_sep) } FORMATTERS);
}
I'm not sure what your problem is, although I think you took the long way to say "I have a function that expects a B object, and I want to pass it a D object."
If you only want objects of a certain exact type, don't accept anything else:
use Carp qw(croak);
sub Bf {
croak "Bad object! I only like B" unless ref $_[0] eq 'B';
...
}
But, that's a bad idea. A derived class should be just as good as the base class. The clean solution is to not care what type you get.
sub Bf {
croak "Bad object! Doesn't respond to foo!" unless $_[0]->can('foo');
...
}
Since this Bf method works with the base class, why would it look for something in some derived class it didn't know about? If the derived class has changed the interface and no longer acts like its parent, then maybe it's isn't a good fit for inheritance. There are many problems like this that are solved by a different architecture.
I think you'll have to come up with a concrete example where the derived class wouldn't work.
It sounds like for some reason you need your D object to behave like a B object, but at the same time not like a D object. As the existing answers and comments indicate, it's a very common to use a sub-class where the base class is expected, and most algorithms shouldn't care whether what you actually passed is D or B. The only reason I can think of why you would want otherwise is that D overrides (redefines) some methods in an incompatible way, and you want the methods from B instead.
package Dog;
sub new {
my ($class, %args) = #_;
return bless \%args, $class;
}
sub bark { print "Bark!\n"; }
package Dingo;
use parent 'Dog';
sub bark { print "...\n"; }
package main;
my $dingo = Dingo->new;
$dingo->bark; # "..."
(n.b., I've left off the recommended use strict; and use warnings; for terseness, they should be used in all packages)
You may be aware from reading perldoc perlootut and perldoc perlobj that an object in Perl is just a blessed reference of some sort; in the example above, we use a hash reference. If you are trying to get the "attributes" that only exist in B, I think you would have to write some sort of translation method. But, if you care about the methods that exist in B, all you have to do is re-bless it into the parent class.
my $dingo = Dingo->new;
$dingo->bark; # "..."
bless $dingo, "Dog";
$dingo->bark; # "Bark!"
Note that bless does not return a new reference, but modifies that reference in-place; if you want it to behave like a Dingo again, you have to bless it back.
Perhaps more conveniently you can define a method to create a copy for you and bless it into the appropriate class:
package Dog;
sub as_dog {
my ($self) = #_;
# The {} below create a shallow copy, i.e., a new reference
return bless { %{$self} }, __PACKAGE__;
}
#...
package main;
my $dingo = Dingo->new;
$dingo->bark; # ...
$dingo->as_dog->bark; # Bark!
$dingo->bark; # ...
While there doesn't seem to be a perfect solution, temporary "re-blessing" the object seems to get quite close to what is asked for:
sub Bf($) # expects a "B" object (or descendant of "B" (like "D"))
{
my $B = shift;
my $type = ref($B); # save original type
die "unexpected type $type" unless ($B->isa('B'));
bless $B, 'B'; # restrict to "B"'s features
$B->whatever(...);
#...
bless $B, $type; # restore original type
}
I am creating a module that has some fairly heavily nested hashes. The hash needs to be semi-regularly modified by the module, which unfortunately rules out using Map.
Generally, a branch of the nested hash will be returned to users of the module [1], and the simplest thing to do is to just return that nested hash, e.g.:
return %data{$branch}{$subbranch}
# ↪︎ %(subsubbranch1 => ... , subsubbranch2 => ... )
However, the nature of containers like arrays or hashes is that while you can make them read-only, the key/values can still be modified. The module users though should not actually modify those values for a number of reasons. Coercing to Map won't help, because if any of the values are also containers, they too will be modifiable.
My first thought was to subclass Hash (or otherwise make a custom Associative), but autovivification by default still goes to Hash. That, however, can be easily solved by overriding both AT-KEY and ASSIGN-KEY so that the AT-KEY returns an instance of the subclass if the key doesn't already exist:
class ProtectedHash is Hash {
has %!hash = ();
method EXISTS-KEY ($key) { %!hash{$key}:exists }
method ASSIGN-KEY ($key, \value) { %!hash{$key} = value }
method AT-KEY ($key) {
%!hash{$key} := ProtectedHash.new unless %!hash{$key}:exists;
%!hash{$key};
}
}
What I'd like to do is to fail if the ASSIGN-KEY (or the autovivification part of AT-KEY) is called from outside my module. I thought about using something like $?MODULE but that would be set at compile time and always be true. It looks like I can shimmy off of Backtrace a bit and check for the name of the file that called, but how consistent can I assume the call trace to those two functions?
For example, for ASSIGN-KEY I've got:
method ASSIGN-KEY ($key, \value) {
my #trace = Backtrace.new.list[3..*];
# The first three can be ignored:
# 0: code at ...Backtrace.pm6
# 1: method new at ...Backtrace.pm6
# 2: method AT-KEY at ...ThisFile.pm6
if/unless ??? {
%!hash{$key} = value
}
}
AT-KEY is normally called by the sub postcircumfix<{ }> (in which case #trace[0] can be ignored, and trace[1] would be the one of interest) but could also be, albeit rarely, called directly, in which case trace[0] is where I'd want to verify the file name.
Are there any other common ways in which AT-KEY or ASSIGN-KEY might be called? Or should check those two steps account for 99.9% of calls to those methods? [2]
[1] There are only a few subx4 branches that a user might want to manipulate, and so I figure it's best to provide them with the necessarily-slower .Hash method for when they really need it than to assume they always need a manipulable container. At times these may be called enough (particularly via a get-branch($foo){$subbranch}{$subsubbranch} pattern), that the addition overhead in creating a deepclone of the Hash becomes decently consequential.
[2] I'm not too concerned about preventing ANY access (although I'm certainly curious if that's possible purely via subclassing), because I'm sure that a fairly industrious coder could always figure something out, but I'd like to catch the most common ones as a way of saying "Can't touch this!" (cue the 90's music…) and provide an Awesome error message.
It's probably easier to achieve this by returning something wrapping the original Array or Hash, or alternatively using but to do a shallow copy and mix in to it (which means you retain the original type).
We can declare a role like this:
role Can'tTouchThis {
method AT-KEY(|) {
untouchable callsame
}
method ASSIGN-KEY(|) {
die "Cannot assign to this";
}
method AT-POS(|) {
untouchable callsame
}
method ASSIGN-POS(|) {
die "Cannot assign to this";
}
}
Where the sub untouchable is defined as:
multi untouchable(Positional \p) {
p but Can'tTouchThis
}
multi untouchable(Associative \a) {
a but Can'tTouchThis
}
multi untouchable(\o) {
o
}
Thus handling nested data structures by - on access - creating a read-only facade to those too.
Here's an example and some test cases to illustrate the effect:
class Example {
has %!foo = a => [ 1, 2, [ 3, 4] ], b => { c => { d => 42, e => 19 }, f => 100 };
method get($sym) {
untouchable %!foo{$sym}
}
}
given Example.new {
use Test;
# Positional cases
is .get('a')[0], 1;
is .get('a')[2][1], 4;
dies-ok { .get('a')[1] = 42 };
is .get('a')[1], 2;
# Associative cases
is .get('b')<c><d>, 42;
dies-ok { .get('b')<f> = 99 };
dies-ok { .get('b')<c><d> = 99 };
is .get('b')<f>, 100;
is .get('b')<c><d>, 42;
# Auto-viv also doesn't work
dies-ok { .get('a')[4]<a> = 99 };
dies-ok { .get('a')[4][0] = 99 };
}
Remove the untouchable call in the get method to see the majority of the tests here fail due to lack of protection.
The solution I ultimately employed served my needs, and I'm posting it here for those who may encounter similar situations. (The answer with role mixing unfortunately doesn't survive binding)
My ultimate approach was to worry the most about unintended editing. To protect against this, I created an Associative-type class called DB-Item that internally has a hash. The AT-KEY method returns the item from the hash if it exists, but ASSIGN-KEY and BIND-KEY simply immediately fail with an appropriate error message. The only other method is ADD-TO-DATABASE. That method handles adds leafs/branches depending on what it's passed (and in general end users should be wary of using all caps methods directly). Since branches can be of different lengths, this also greatly simplifies the initial DB creation:
class DB-Item does Associative {
has %!hash = ();
my $epitaph = "Modification of the database is not a good idea:\n" ~
" - Use .clone if you want to get a editable branch.\n" ~
" - If you really know what you're doing, use .ADD-TO-DATABASE";
method ADD-TO-DATABASE (*#branch) {
if #branch == 2 {
%!hash{#branch.head} = #branch.tail
}else{
%!hash{#branch.head} = DB-Item.new;
%!hash{#branch.head}.ADD-TO-DATABASE(#branch[1..*]);
}
}
method ASSIGN-KEY(|) is hidden-from-backtrace { die $epitaph }
method BIND-KEY(|) is hidden-from-backtrace { die $epitaph }
method EXISTS-KEY($key) { %!hash{$key}:exists }
method AT-KEY($key) { %!hash{$key}:exists ?? %!hash{$key} !! Nil }
method clone { ... }
}
I need to pass some code from an external program into a class.
In a generic module I have (for simplicity reduced to silliness)
class A {
has &.hl;
submethod BUILD( :&!hl ) {}
}
Elsewhere in a program, I have
use A;
my &hl = -> $st {
my $p = shell "hl $st", :in,:out;
$p.out.slurp
};
my $statement = 'my $raku-variable = "Helloooo";'
my $first = &hl($statement);
my A $a .= new(:&hl);
my $second = $a.hl( $statement );
$first will be processed and will contain the expected results.
At $second, I will get a runtime error
Too many positionals passed; expected 1 argument but got 2
Clearly the routine in the class is being provided both the invocant and the parameter $s.
Rewriting the class to provide a custom accessor:
class A {
has &!hl;
submethod BUILD( :&!hl ) {}
method process-it( Str $s --> Str ) { &!hl( $s ) }
}
# elsewhere
my $second = $a.process-it( $statement );
Then both $first and $second run without error and will contain the same results.
When hl is accessed inside the class, no invocant is added, but if it is not declared as &.hl then it is not visible outside the class.
My question is therefore: Is there another way to create a public object code variable that does not automagically add the invocant as a variable to the code? Other than creating a separate accessor method.
Here is short bash script hl for illustration
#! /bin/bash
echo '<div class="statement">'$1'</div>'
Here is a full Raku program
use v6.c;
class A {
has &!highlighter; # also tried with has &highlighter
submethod BUILD( :&!highlighter ) {}
method process-it( Str $s --> Str ) {
&!highlighter( $s )
}
}
sub MAIN() {
my #strings = 'my $v = "Hello World";', 'my $w = $v.raku;';
my $proc;
my $proc-supply;
my &highlighter = -> $s {
my $p = shell "./hl '$s' ", :in,:out;
$p.out.slurp
}
for #strings {
say .&highlighter
}
my A $a .= new(:&highlighter);
for #strings { say $a.highlighter($_) }
# own accessor
for #strings { say $a.process-it($_) }
}
has $!hl declares a private attribute. has $.hl declares a public attribute.
By public I mean it creates a method of the same name that returns it, and it adds it to the BUILD/gist/perl/Capture [sub]methods.
class A {
has &.hl;
}
This is effectively the same as:
class A {
has &!hl;
submethod BUILD ( :&!hl ){}
method hl (){ &!hl } # return the code object
method perl (){
"A.new(hl => $!hl.perl())"
}
method gist (){ self.perl }
method Capture () {
\( :&!hl )
}
}
So when you call A.hl it returns the code object that is stored in &!hl.
You can deal with this in a few ways.
Just call it “twice”.
$a.hl()(42)
$a.hl().(42)
$a.hl.(42)
Have an additional method that uses it.
method call-it ( |C ){
&!hl( |C )
}
$a.call-it( 42 )
my &hl = $a.hl;
Note that I used |C to avoid dealing with signatures entirely.
It might make sense for you to have a signature and deal with it like you have.
Override the automatically generated method by adding it yourself.
method hl ( |C ){
&!hl( |C )
}
$a.hl( 42 )
By overriding it, all of the other changes that making it a public attribute are still done for you.
So there will be no need to create a BUILD submethod.
When you override it, that means that is rw has no effect. It also means that
there is no way for outside code to retrieve the code object itself.
There are ways to deal with that if you need to.
If you don't ever need to return the value in &!hl then just leave it like it is above.
If the code object is never called with zero positional arguments.
multi method hl (){ &!hl }
multi method hl ( |C ){
&!hl( |C )
}
$a.hl; # returns the value in $!hl
$a.hl(); # returns the value in $!hl
$a.hl( 42 ); # calls &!hl(42)
Note that there is no way for a method to differentiate between .hl and .hl().
You could also use a named argument.
multi method hl ( :code($)! ){ &!hl }
multi method hl ( |C ){
&hl( |C )
}
$a.hl(:code); # returns the value in &!hl
$a.hl; # calls &!hl()
$a.hl(); # calls &!hl()
$a.hl( 42 ); # calls &!hl(42)
You could do nothing to make it easier to get the code object, and just have them use subsignature parsing to get the attribute.
(This is why the Capture method gets created for you)
class A {
has &.hl;
method hl ( |C ){
&!hl( |C )
}
}
sub get-hl ( A $ ( :&hl ) ){ &hl }
my &hl = get-hl($a);
my &hl = -> A $ ( :&hl ){ &hl }( $a );
my &hl = $a.Capture{'hl'};
TL;DR There is no way to directly access an attribute outside the source code of the class in which it is declared. The only way to provide access is via a separate public accessor method. This answer hopefully clears up confusion about this. Other answers lay out your options.
Why you get a Too many positionals passed; error message
The code has &!hl; declares an attribute, &!hl.
The code has &.hl; does the same but also generates a method, .hl that's a public accessor to the attribute with the same name. Like all such generated accessors, it expects a single argument, the invocant, and no others.
my $second = $a.hl( $statement )
This code calls the method hl. Raku passes the value on the left of the dot ($a) as a first argument -- the invocant. But you've also added a $statement argument. So it passes that too.
Hence the error message:
Too many positionals passed; expected 1 argument but got 2
When hl is accessed inside the class, no invocant is added
It's not because it's accessed inside the class. It's because you don't call it as a method:
method process-it( Str $s --> Str ) { &!hl( $s ) }
The &!hl( $s ) code is a sub style call of the routine held in the &!hl attribute. It gets one argument, $s.
Is there another way to create a public object code variable that does not automagically add the invocant as a variable to the code?
The problem is not that Raku is automagically adding an invocant.
Other than creating a separate accessor method.
There is no way to directly access an attribute outside the source code of the class in which it is declared. The only way to provide access is via a separate public accessor method. This answer hopefully clears up confusion about this. Other answers lay out your options.
The problem is that the accessor returns the attribute, that happens to be a Callable. Only then do you want to call the return value of the accessor with parameters. This is essentially what you're doing by creating your own accessor.
You don't have to actually create your own accessor. Just add a extra parentheses (indicating you're calling the accessor without any extra arguments), and then the parentheses for the values you actually want to pass:
class A {
has &.a = *.say; # quick way to make a Callable: { .say }
}
A.new.a()(42); # 42
Or if you don't like parentheses so much, consider the method invocation syntax, as timotimo pointed out:
A.new.a.(42); # 42
Suppose I have the following code implemented in Itcl.
package require Itcl
itcl::class A {
constructor {} { puts $this }
destructor {}
public method Print {} { puts "ok" }
}
itcl::class B {
constructor {} { }
destructor {}
public method returnA {} { return [A #auto] }
}
B b ;# create an instance of class B
set obj [b returnA] ; #assign return value to obj
$obj Print ;# should treat obj as an A type and print ok
Now, I get the following error:
invalid command name "0"
while executing
"$obj Print"
I understood that I need to add scopes to my variable or to the Print command in order to invoke Print method that associated to class A.
But I don't really know how.
I also read the following post:
How to get a reference on the Itcl class member variable?
But it doesn't says there how to treat the return value as a specific class type variable
You have to qualify the name of the yet to be created instance of class A:
A [namespace current]::#auto
Otherwise, the name of the created object is returned in an unqualified manner (0, a0, ...), which cannot be resolved to a Tcl command for the scope of the caller of returnA.