I am trying to implement a trigger on write access to a perl6 Class attribute. I cannot figure out the cause of the error...
... I got this notion from How does one write custom accessor methods in Perl6?
1 #!/usr/bin/env perl6
2
3 class MeasureSP {
4
5 has Real $!value;
6 has Str $.units;
7
8 submethod BUILD( :$!value, :$!units ) {}
9
10 method value( Real $newval? ) is rw {
11 return Proxy.new:
12 FETCH => sub ($) { $!value },
13 STORE => sub ($, $newval) { $!value = $newval },
14 }
15
16 }
17 my MeasureSP $m-sp = MeasureSP.new( value => 23, units => 'metres' );
18 say $m-sp.units; #metres
19 say $m-sp.value; #23
20 $m-sp.value = 1;
21 # Cannot assign to a readonly variable or a value
22 #in block <unit> at ./retry.p6 line 20
This behaviour seems to have changed - this code was working fine back in June 18 - but I want to be sure I am in step with the latest thinking.
Any help would be very much appreciated !
Either remove the return:
method value( Real $newval? ) is rw {
Proxy.new:
FETCH => sub ($) { $!value },
STORE => sub ($, $newval) { $!value = $newval },
}
Or use return-rw if you really want to be explicit:
method value( Real $newval? ) is rw {
return-rw Proxy.new:
FETCH => sub ($) { $!value },
STORE => sub ($, $newval) { $!value = $newval },
}
The problem is that return will strip away any item container, so just putting is rw on the method is not enough on its own.
As to why the posted code used to work, but no longer does: Rakudo releases in the last couple of months have included a fix for a bug that meant Proxy was treated differently than Scalar in routine return handling. The Proxy was not stripped in places it should have been; now it reliably is.
Related
I am going to use Hash::Merge as an example. Consider:
use v6;
use Hash::Merge; # <-- imports all symbols marked with "is export" from Hash::Merge
my %hash1 = a1 => [1, 2, 3], b => "xxx", c => { ca => 1 }, e => 5;
my %hash2 = a1 => [1, 5, 3], b => "yyyy", c => { ca => 5, f => "a" }, d => 4;
my %res = merge-hash(%hash1, %hash2, :no-append-array);
Suppose I do not want to pollute my name space when using a module (here Hash::Merge is used as an example). I could achive this in Perl 5 by specifying an empty argument list to use:
use Hash::Merge (); # <-- No symbols will be imported into the current namespace
Then I would call the sub routine merge-hash using its fully qualified name:
Hash::Merge::merge-hash.
According to this bug report it seems like this is not possible in Perl 6. Is this correct?
To load a module without importing, use need instead:
need Hash::Merge;
In the case of the module in question, it does not declare the things it exports with our, which unfortunately means that calling it as:
Hash::Merge::merge-hash(...)
Will not work, since it's not installed in the package. However, it is still possible to dig the symbol out of the exports manually:
need Hash::Merge;
say Hash::Merge::EXPORT::DEFAULT::merge-hash({ a => 1 }, { b => 2 })
And, for more convenience, it can be aliased:
need Hash::Merge;
my constant &merge-hash = &Hash::Merge::EXPORT::DEFAULT::merge-hash;
say merge-hash({ a => 1 }, { b => 2 });
There is a speculated syntax along the lines of use Hash::Merge :MY<&merge-hash>, which is not implemented in current Perl 6 versions, but would probably have the same semantics as the constant trick shown here.
A simple way to deal with this is to just put the use of the module in a block.
{ use Hash::Merge }
Since the {} defines a scope, nothing escapes it.
You can get it so that something can escape by placing it in a do block.
do { use Hash::Merge }
What you can do then is have it so that the values you care about get stored in the correct places.
my &merge-hash = do { use Hash::Merge; &merge-hash }
my (&merge-hash,&merge-hashes) = do { use Hash::Merge; (&merge-hash, &merge-hashes) }
Another option is to just place it in as small a scope as possible.
my %a = a => 1;
my %b = b => 2;
my %c;
{
use Hash::Merge;
%c := merge-hash %a, %b
}
or
my %c := do {
use Hash::Merge;
merge-hash %a, %b
}
(The binding operator := was just used because the result of merge-hash is already a hash.)
Let's say I have the following module:
module Simple-Mod;
#| Calculate the nth fibonacci number.
multi fib( 0 ) { 1 }
multi fib( 1 ) { 1 }
multi fib( Int $n where * > 1 ) {
fib($n - 2 ) + fib($n - 1);
}
#| Say hello to a person.
sub hello( $person ) { say "Hello, $person!" }
=begin pod
=head1 SYNOPSIS
A really simple module.
=head1 Example
=begin code
use Simple-Mod;
say fib(3); #=> 2
hello("Gina"); #=> Hello, Gina!
=end code
=head1 Subroutines
=end pod
At the moment, when I extract the Pod from this module, I obtain this:
sub fib(
Int $ where { ... },
)
Calculate the nth fibonacci number.
sub hello(
$person,
)
Say hello to a person.
SYNOPSIS
A really simple module.
Example
use Simple-Mod;
say fib(3); #=> 2
hello("Gina"); #=> Hello, Gina!
Subroutines
Is it possible to instruct the Pod parsing process to place the
subroutine definitions and comments after the Subroutines header? Like this:
SYNOPSIS
A really simple module.
Example
use Simple-Mod;
say fib(3); #=> 2
hello("Gina"); #=> Hello, Gina!
Subroutines
sub fib(
Int $ where { ... },
)
Calculate the nth fibonacci number.
sub hello(
$person,
)
Say hello to a person.
I could probably place everything from the =begin pod to the =head1 Subroutines (followed by =end pod) directive at the top of file and then the usual code with the declarator blocks. However I'd like to keep all the Pod at the bottom of the file if possible.
By tinkering with the Pod::To::Text module, I came up a somewhat hackish solution
that is far from robust. It solely depends on a new subroutine and some changes to the render, pod2text and heading2text routines:
unit class Pod::To::Textx;
my $top-pod = Any;
method render($pod, Bool $declarator-displacement = False) {
$top-pod = $pod if $declarator-displacement;
pod2text($pod)
}
sub pod2text($pod) is export {
# other code
when Pod::Block::Declarator { if $top-pod { succeed }
else { declarator2text($pod) }
}
# remaining code
}
sub add-code-info($pod) {
return pod2text($pod.contents) unless $top-pod;
if $pod.contents.head.contents.lc.contains("routines") {
pod2text($pod.contents) ~
#($top-pod).grep({ $_ ~~ Pod::Block::Declarator })
.map({ "\n\n" ~ declarator2text($_) })
}
}
sub heading2text($pod) {
given $pod.level {
when 1 { add-code-info($pod) }
when 2 { ' ' ~ pod2text($pod.contents) }
default { ' ' ~ pod2text($pod.contents) }
}
}
# rest of code
To render the Pod in a .p6 file and place the declarator blocks underneath a heading level 1 titled Subroutines/Routines, use:
use Pod::To::Textx;
say Text.new().render($=pod, True);
inside the file.
I have a script like the below. Intent is to have different filter methods to filter a list.
Here is the code.
2
3 class list_filter {
4 has #.my_list = (1..20);
5
6 method filter($l) { return True; }
7
8 # filter method
9 method filter_lt_10($l) {
10 if ($l > 10) { return False; }
11 return True;
12 }
13
14 # filter method
15 method filter_gt_10($l) {
16 if ($l < 10) { return False; }
17 return True;
18 }
19
20 # expecting a list of (1..10) to be the output here
21 method get_filtered_list_lt_10() {
22 return self.get_filtered_list(&{self.filter_lt_10});
23 }
24
25 # private
26 method get_filtered_list(&filter_method) {
27 my #newlist = ();
28 for #.my_list -> $l {
29 if (&filter_method($l)) { push(#newlist, $l); }
30 }
31 return #newlist;
32 }
33 }
34
35 my $listobj = list_filter.new();
36
37 my #outlist = $listobj.get_filtered_list_lt_10();
38 say #outlist;
Expecting [1..10] to be the output here. But getting following error.
Too few positionals passed; expected 2 arguments but got 1
in method filter_lt_10 at ./b.pl6 line 9
in method get_filtered_list_lt_10 at ./b.pl6 line 22
in block <unit> at ./b.pl6 line 37
What am I doing wrong here?
Passing a method as a parameter in Perl 6 either requires you to use MOP (Meta-Object Protocol) methods, or pass the method by name (which would then do the lookup for you at runtime).
But why use methods if you're not really doing something with the object in those methods? They might as well be subs then, which you can pass as a parameter.
Perhaps this is best by example:
class list_filter {
has #.my_list = 1..20; # don't need parentheses
sub filter($ --> True) { } # don't need code, signature is enough
# filter sub
sub filter_lt_10($l) { not $l > 10 }
# filter sub
sub filter_gt_10($l) { not $l < 10 }
# private
method !get_filtered_list(&filter_sub) {
#.my_list.grep(&filter_sub);
}
# expecting a list of (1..10) to be the output here
method get_filtered_list_lt_10() {
self!get_filtered_list(&filter_lt_10);
}
}
my $listobj = list_filter.new();
my #outlist = $listobj.get_filtered_list_lt_10();
say #outlist; # [1 2 3 4 5 6 7 8 9 10]
The first sub filter, which only returns a constant value (in this case True), can be represented much more easily in the signature with an empty body.
The filter_lt_10 and filter_gt_10 subs only need the condition negated, hence the use of the not.
The get_filtered_list method is supposed to be private, so make it a private method by prefixing !.
In the get_filtered_list_lt_10 you now need to call get_filtered_list with a ! instead of a .. And you pass the filter_lt_10 sub as a parameter by prefixing the & (otherwise it would be considered a call to the sub without any parameters, which would fail).
Change the get_filtered_listto use the built-in grep method: this takes a Callable block that takes a single parameter and which should return something True to include the value of the list it works upon. Since a sub taking a single parameter is a Callable, we can just specify the sub there directly.
Hope this made sense. I tried to stay as close as possible to the intended semantics.
Some general programming remarks: it feels to me that the naming of the subs is confusing: it feels to me that they should be called filter_le_10 and filter_ge_10, because that's really what they do it appears to me. Also, if you really don't want any ad-hoc filtering, but only filtering from a specific set of predefined filters, you would probably be better of by creating a dispatch table using constants or enums, and use that to indicate which filter you want, rather than encoding this information in the name of yet another method to make and maintain.
Hope this helps.
TL;DR You told P6 what arguments to expect when calling your filter method. Then you failed to pass the agreed argument(s) when you called it. So P6 complained on your behalf. To resolve the issue, either pass the argument(s) you told P6 to expect or stop telling P6 to expect them. :)
The message says expected 2, got 1, rather than expected 1 got 0.
This is because self is implicitly passed and added to the "expected" and "got" totals in this appended bit of message detail, bumping both up by one. (This detail is perhaps Less Than Awesome, i.e. something we should perhaps consider fixing.)
When I run your code on tio I get:
Too few positionals passed; expected 2 arguments but got 1
in method filter at .code.tio line 27
in method print_filtered_list at .code.tio line 12
in block <unit> at .code.tio line 42
The method declaration method filter($l) {...} at line 27 tells P6 to expect two arguments for each .filter method call:
The invocant. (This will be bound to self.) Let's call that argument A.
A positional argument. (This will be bound to the $l parameter). Let's call that argument B.
But in &{self.filter} in line 12, while you provide the .filter method call with an argument A, i.e. an invocant argument, you don't provide an argument B, i.e. a positional argument (after filter, e.g. &{self.filter(42)}).
Hence Too few positionals passed; expected 2 arguments but got 1.
The &{self.method} syntax was new to me, so thanks for that. Unfortunately it doesn't work if parameters are needed. You can use sub as other posters mentioned, but if you need to use methods, you can get a method by calling self.^lookup, which is the use of the meta-object protocol that Elizabeth mentioned. ('^' means you're not calling a method that's part of that class, but rather part of the "shadow" class which contains the main class's guts / implementation details.)
To get a method, use run obj.^lookup(method name), and call it by passing in the object itself (often "self") as the first parameter, then the other parameters. To bind the object to the function so it doesn't need to be explicitly added each time, use the assuming function.
class MyClass {
method log(Str $message) { say now ~ " $message"; }
method get-logger() { return self.^lookup('log').assuming(self); }
}
my &log = MyClass.get-logger();
log('hello'); # output: Instant:1515047449.201730 hello
Found it. this is what worked for me.
3 class list_filter {
4 has #.my_list = (1..20);
5
6 # will be overriding this in derived classes
7 method filter1($l) { return True; }
8 method filter2($l) { return True; }
9
10 # same print method I will be calling from all derived class objects
11 method print_filtered_list($type) {
12 my #outlist = self.get_filtered_list($type);
13 say #outlist;
14 }
15
16 # private
17 method get_filtered_list($type) {
18 my #newlist = ();
19 for #.my_list -> $l {
20 my $f = "filter$type";
21 if (self."$f"($l)) { push(#newlist, $l); }
22 }
23 return #newlist;
24 }
25 }
26
27 class list_filter_lt_10 is list_filter {
28 method filter1($l) {
29 if ($l > 10) { return False; }
30 return True;
31 }
32 method filter2($l) {
33 if ($l > 10) { return False; }
34 if ($l < 5) { return False; }
35 return True;
36 }
37 }
38
39 class list_filter_gt_10 is list_filter {
40 method filter1($l) {
41 if ($l < 10) { return False; }
42 return True;
43 }
44 method filter2($l) {
45 if ($l < 10) { return False; }
46 if ($l > 15) { return False; }
47 return True;
48 }
49 }
50
51 my $listobj1 = list_filter_lt_10.new();
52 $listobj1.print_filtered_list(1);
53 $listobj1.print_filtered_list(2);
54
55 my $listobj2 = list_filter_gt_10.new();
56 $listobj2.print_filtered_list(1);
57 $listobj2.print_filtered_list(2);
58
Output:
./b.pl6
[1 2 3 4 5 6 7 8 9 10]
[5 6 7 8 9 10]
[10 11 12 13 14 15 16 17 18 19 20]
[10 11 12 13 14 15]
piojo's answer looks like it would work (though I haven't tried it).
Another approach to turning a method into a variable is to use indirection:
class Foo {
method bar($a) {
$a * 2
}
}
sub twice(&f, $x) {
f f $x
}
my $foo = Foo.new();
say twice {$foo.bar: $^a}, 1
I have a script like the below. Intent is to have different filter methods to filter a list.
Here is the code.
2
3 class list_filter {
4 has #.my_list = (1..20);
5
6 method filter($l) { return True; }
7
8 # filter method
9 method filter_lt_10($l) {
10 if ($l > 10) { return False; }
11 return True;
12 }
13
14 # filter method
15 method filter_gt_10($l) {
16 if ($l < 10) { return False; }
17 return True;
18 }
19
20 # expecting a list of (1..10) to be the output here
21 method get_filtered_list_lt_10() {
22 return self.get_filtered_list(&{self.filter_lt_10});
23 }
24
25 # private
26 method get_filtered_list(&filter_method) {
27 my #newlist = ();
28 for #.my_list -> $l {
29 if (&filter_method($l)) { push(#newlist, $l); }
30 }
31 return #newlist;
32 }
33 }
34
35 my $listobj = list_filter.new();
36
37 my #outlist = $listobj.get_filtered_list_lt_10();
38 say #outlist;
Expecting [1..10] to be the output here. But getting following error.
Too few positionals passed; expected 2 arguments but got 1
in method filter_lt_10 at ./b.pl6 line 9
in method get_filtered_list_lt_10 at ./b.pl6 line 22
in block <unit> at ./b.pl6 line 37
What am I doing wrong here?
Passing a method as a parameter in Perl 6 either requires you to use MOP (Meta-Object Protocol) methods, or pass the method by name (which would then do the lookup for you at runtime).
But why use methods if you're not really doing something with the object in those methods? They might as well be subs then, which you can pass as a parameter.
Perhaps this is best by example:
class list_filter {
has #.my_list = 1..20; # don't need parentheses
sub filter($ --> True) { } # don't need code, signature is enough
# filter sub
sub filter_lt_10($l) { not $l > 10 }
# filter sub
sub filter_gt_10($l) { not $l < 10 }
# private
method !get_filtered_list(&filter_sub) {
#.my_list.grep(&filter_sub);
}
# expecting a list of (1..10) to be the output here
method get_filtered_list_lt_10() {
self!get_filtered_list(&filter_lt_10);
}
}
my $listobj = list_filter.new();
my #outlist = $listobj.get_filtered_list_lt_10();
say #outlist; # [1 2 3 4 5 6 7 8 9 10]
The first sub filter, which only returns a constant value (in this case True), can be represented much more easily in the signature with an empty body.
The filter_lt_10 and filter_gt_10 subs only need the condition negated, hence the use of the not.
The get_filtered_list method is supposed to be private, so make it a private method by prefixing !.
In the get_filtered_list_lt_10 you now need to call get_filtered_list with a ! instead of a .. And you pass the filter_lt_10 sub as a parameter by prefixing the & (otherwise it would be considered a call to the sub without any parameters, which would fail).
Change the get_filtered_listto use the built-in grep method: this takes a Callable block that takes a single parameter and which should return something True to include the value of the list it works upon. Since a sub taking a single parameter is a Callable, we can just specify the sub there directly.
Hope this made sense. I tried to stay as close as possible to the intended semantics.
Some general programming remarks: it feels to me that the naming of the subs is confusing: it feels to me that they should be called filter_le_10 and filter_ge_10, because that's really what they do it appears to me. Also, if you really don't want any ad-hoc filtering, but only filtering from a specific set of predefined filters, you would probably be better of by creating a dispatch table using constants or enums, and use that to indicate which filter you want, rather than encoding this information in the name of yet another method to make and maintain.
Hope this helps.
TL;DR You told P6 what arguments to expect when calling your filter method. Then you failed to pass the agreed argument(s) when you called it. So P6 complained on your behalf. To resolve the issue, either pass the argument(s) you told P6 to expect or stop telling P6 to expect them. :)
The message says expected 2, got 1, rather than expected 1 got 0.
This is because self is implicitly passed and added to the "expected" and "got" totals in this appended bit of message detail, bumping both up by one. (This detail is perhaps Less Than Awesome, i.e. something we should perhaps consider fixing.)
When I run your code on tio I get:
Too few positionals passed; expected 2 arguments but got 1
in method filter at .code.tio line 27
in method print_filtered_list at .code.tio line 12
in block <unit> at .code.tio line 42
The method declaration method filter($l) {...} at line 27 tells P6 to expect two arguments for each .filter method call:
The invocant. (This will be bound to self.) Let's call that argument A.
A positional argument. (This will be bound to the $l parameter). Let's call that argument B.
But in &{self.filter} in line 12, while you provide the .filter method call with an argument A, i.e. an invocant argument, you don't provide an argument B, i.e. a positional argument (after filter, e.g. &{self.filter(42)}).
Hence Too few positionals passed; expected 2 arguments but got 1.
The &{self.method} syntax was new to me, so thanks for that. Unfortunately it doesn't work if parameters are needed. You can use sub as other posters mentioned, but if you need to use methods, you can get a method by calling self.^lookup, which is the use of the meta-object protocol that Elizabeth mentioned. ('^' means you're not calling a method that's part of that class, but rather part of the "shadow" class which contains the main class's guts / implementation details.)
To get a method, use run obj.^lookup(method name), and call it by passing in the object itself (often "self") as the first parameter, then the other parameters. To bind the object to the function so it doesn't need to be explicitly added each time, use the assuming function.
class MyClass {
method log(Str $message) { say now ~ " $message"; }
method get-logger() { return self.^lookup('log').assuming(self); }
}
my &log = MyClass.get-logger();
log('hello'); # output: Instant:1515047449.201730 hello
Found it. this is what worked for me.
3 class list_filter {
4 has #.my_list = (1..20);
5
6 # will be overriding this in derived classes
7 method filter1($l) { return True; }
8 method filter2($l) { return True; }
9
10 # same print method I will be calling from all derived class objects
11 method print_filtered_list($type) {
12 my #outlist = self.get_filtered_list($type);
13 say #outlist;
14 }
15
16 # private
17 method get_filtered_list($type) {
18 my #newlist = ();
19 for #.my_list -> $l {
20 my $f = "filter$type";
21 if (self."$f"($l)) { push(#newlist, $l); }
22 }
23 return #newlist;
24 }
25 }
26
27 class list_filter_lt_10 is list_filter {
28 method filter1($l) {
29 if ($l > 10) { return False; }
30 return True;
31 }
32 method filter2($l) {
33 if ($l > 10) { return False; }
34 if ($l < 5) { return False; }
35 return True;
36 }
37 }
38
39 class list_filter_gt_10 is list_filter {
40 method filter1($l) {
41 if ($l < 10) { return False; }
42 return True;
43 }
44 method filter2($l) {
45 if ($l < 10) { return False; }
46 if ($l > 15) { return False; }
47 return True;
48 }
49 }
50
51 my $listobj1 = list_filter_lt_10.new();
52 $listobj1.print_filtered_list(1);
53 $listobj1.print_filtered_list(2);
54
55 my $listobj2 = list_filter_gt_10.new();
56 $listobj2.print_filtered_list(1);
57 $listobj2.print_filtered_list(2);
58
Output:
./b.pl6
[1 2 3 4 5 6 7 8 9 10]
[5 6 7 8 9 10]
[10 11 12 13 14 15 16 17 18 19 20]
[10 11 12 13 14 15]
piojo's answer looks like it would work (though I haven't tried it).
Another approach to turning a method into a variable is to use indirection:
class Foo {
method bar($a) {
$a * 2
}
}
sub twice(&f, $x) {
f f $x
}
my $foo = Foo.new();
say twice {$foo.bar: $^a}, 1
How does one write custom accessor methods in Perl6?
If I have this class:
class Wizard {
has Int $.mana is rw;
}
I can do this:
my Wizard $gandalf .= new;
$gandalf.mana = 150;
Let's say I want to add a little check to a setter in my Perl6 class without giving up the $gandalf.mana = 150; notation (in other words, I don't want to write this: $gandalf.setMana(150);). The program should die, if it tries to set a negative mana. How do I do this? The Perl6 documentation just mentions it is possible to write custom accessors, but does not say how.
With more recent versions of Rakudo there is a subset named UInt that restricts it to positive values.
class Wizard {
has UInt $.mana is rw;
}
So that you're not stuck in a lurch if you need to something like this; here is how that is defined:
( you can leave off the my, but I wanted to show you the actual line from the Rakudo source )
my subset UInt of Int where * >= 0;
You could also do this:
class Wizard {
has Int $.mana is rw where * >= 0;
}
I would like to point out that the * >= 0 in the where constraint is just a short way to create a Callable.
You could have any of the following as a where constraint:
... where &subroutine # a subroutine that returns a true value for positive values
... where { $_ >= 0 }
... where -> $a { $a >= 0 }
... where { $^a >= 0 }
... where $_ >= 0 # statements also work ( 「$_」 is set to the value it's testing )
( If you wanted it to just not be zero you could also use ... where &prefix:<?> which is probably better spelled as ... where ?* or ... where * !== 0 )
If you feel like being annoying to people using your code you could also do this.
class Wizard {
has UInt $.mana is rw where Bool.pick; # accepts changes randomly
}
If you want to make sure the value "makes sense" when looking at all of the values in the class in aggregate, you will have to go to a lot more work.
( It may require a lot more knowledge of the implementation as well )
class Wizard {
has Int $.mana; # use . instead of ! for better `.perl` representation
# overwrite the method the attribute declaration added
method mana () is rw {
Proxy.new(
FETCH => -> $ { $!mana },
STORE => -> $, Int $new {
die 'invalid mana' unless $new >= 0; # placeholder for a better error
$!mana = $new
}
)
}
}
You can get the same accessor interface that saying $.mana provides by declaring a method is rw. Then you can wrap a proxy around the underlying attribute like so:
#!/usr/bin/env perl6
use v6;
use Test;
plan 2;
class Wizard {
has Int $!mana;
method mana() is rw {
return Proxy.new:
FETCH => sub ($) { return $!mana },
STORE => sub ($, $mana) {
die "It's over 9000!" if ($mana // 0) > 9000;
$!mana = $mana;
}
}
}
my Wizard $gandalf .= new;
$gandalf.mana = 150;
ok $gandalf.mana == 150, 'Updating mana works';
throws_like sub {
$gandalf.mana = 9001;
}, X::AdHoc, 'Too much mana is too much';
Proxy is basically a way to intercept read and write calls to storage and do something other than the default behavior. As their capitalization suggests, FETCH and STORE are called automatically by Perl to resolve expressions like $gandalf.mana = $gandalf.mana + 5.
There's a fuller discussion, including whether you should even attempt this, at PerlMonks. I would recommend against the above -- and public rw attributes in general. It's more a display of what it is possible to express in the language than a useful tool.