Theoretically, you can mix in a role into an object in runtime. So I am trying to do this with a function:
my &random-f = -> $arg { "Just $arg" };
say random-f("boo");
role Argable {
method argh() {
self.CALL-ME( "argh" );
}
}
&random-f does Argable;
say random-f.argh;
Within the role, I use self to refer to the already defined function, and CALL-ME to actually call the function within the role. However, this results in the following error:
Too few positionals passed; expected 1 argument but got 0
in block <unit> at self-call-me.p6 line 5
I really have no idea who's expecting 1 argument. Theoretically, it should be the CALL-ME function, but who knows. Eliminating the self. yields a different error: CALL-ME used at line 11. Adding does Callable to Argable (after putting self back) results in the same error. Can this be done? Any idea of how?
There's two things incorrect in your code:
say random-f.argh; # *call* random-f and then call .argh on the result
You want to call .argh on the Callable so:
say &random-f.argh;
Secondly, you should just be able to call self: you can tweak this in the signature of the .argh method:
method argh(&self:) {
So the final code becomes:
my &random-f = -> $arg { "Just $arg" };
say random-f("boo");
role Argable {
method argh(&self:) {
self( "argh" );
}
}
&random-f does Argable;
say &random-f.argh;
Related
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
i'm trying to do something like this:
-- source file 1
my $queue = Thread::Queue->new();
MyModules::populateQueue(<pass $queue->enqueue method reference);
...
-- package file
package MyModules
sub populateQueue {
my $enqueue = $_[0];
my $item = <find item to add to queue>;
$enqueue->($item);
...
first, i'm not able to add "bless" to Thread::Queue
i've tried a couple of suggestions i found in stackoverflow:
my $enqueueRef = $queue->can('enqueue');
MyModules::populateQueue(\&enqueueRef); <--- fails in the package at line
$enqueue->($item) with undefined subroutine
MyModules::populateQueue(\&queue->enqueue) <-- same failure as above
any idea how to pass a method of an object as a parameter to a function that can then be used in the function?
Perl doesn't have a concept of a bound method reference. my $enqueue = $object->can('method') will return a code ref to a method if it exists, but the code ref isn't bound to that particular object – you still need to pass it as the first argument ($queue->$enqueue($item) or $enqueue->($queue, $item)).
To pass a bound method, the correct solution is to use an anonymous sub that wraps the method call:
populate_queue(sub { $queue->enqueue(#_) });
I am calling a method as follows:
$self->class->method
I would like to pass a reference to this method as a parameter into a subroutine.
I have tried
\&{ $self->class->method }
but I get the following error:
Unable to create sub named ""
Any ideas on how to do this?
You can take a reference to static class method, but in your case you could use anonymous closure to achieve similar,
my $ref = sub { $self->class->method };
# ..
my $result = $ref->();
The class method is a little strange. I would expect a method with a name like that to return the class string, but it clearly returns an object as it has a method method.
I recommend that you use UNIVERSAL::can, which returns a reference to the given method if it exists. So you can write code like this
my $method_ref = $self->class->can('method');
mysub($method_ref);
You can also use the curry module to achieve this:
use curry;
my $mref = $self->class->curry::method;
I'm using MooseX::Declare and methods, which uses MooseX::Method::Signatures. Let's say I have a class 'foo' with a method 'bar', and I've implemented it like:
class foo {
method bar (Str $str, Bool :$flag = 1) {
# ....
}
}
I now want to write a front-end interface that asks a user what class they want to use, what method on that class they want to use, and then what options to the method they want. I can do the first two things, so let's say the user has now chosen class foo and method bar.
But how do I find out that method bar takes a string as the first argument, and a flag => bool key value pair that defaults to 1? My code needs to know this so I can then ask the user to supply these things.
First, get the method meta object:
my $method = $class->meta->find_method_by_name( $method_name );
Then, make sure it's a signaturey method:
confess "not method with a signature!"
unless $method->isa('MooseX::Method::Signatures::Meta::Method');
Get its signature:
my $sig = $method->parsed_signature;
Then look at $sig's named_params and positional_params as detailed in the Parse::Method::Signatures::Sig docs.
To find parsed_signature, I had to look at the source to MooseX::Method::Signatures::Meta::Method… so be wary when you do this.