Object Hash Lookup with `eqv` - raku

Is there a way to use eqv to lookup a hash value without looping over the key-value pairs when using object keys?
It is possible to use object keys in a hash by specifying the key's type at declaration:
class Foo { has $.bar };
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';
However, key lookup uses the identity operator === which will return the value only if it is the same object and not an equivalent one:
my Foo $a-prime .= new(:bar(1));
say $a eqv $a-prime; # True
say $a === $a-prime; # False
say %h{$a}; # A
say %h{$a-prime}; # (Any)

Looking at the documentation for "===", the last line reveals that the operator is based on .WHICH and that "... all value types must override method WHICH." This is why if you create two separate items with the same string value, "===" returns True.
my $a = "Hello World";
my $b = join " ", "Hello", "World";
say $a === $b; # True even though different items - because same value
say $a.WHICH ; # "Str|Hello World"
say $b.WHICH ; # (same as above) which is why "===" returns True
So, instead of creating your own container type or using some of the hooks for subscripts, you could instead copy the way "value types" do it - i.e. butcher (some what) the idea of identity. The .WHICH method for strings shown above simply returns the type name and contents joined with a '|'. Why not do the same thing;
class Foo {
has $.bar;
multi method WHICH(Foo:D:) { "Foo|" ~ $!bar.Str }
}
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';
my Foo $a-prime .= new(:bar(1));
say $a eqv $a-prime; # True
say $a === $a-prime; # True
say %h{$a}; # A
say %h{$a-prime}; # A
There is a small cost, of course - the concept of identity for the instances of this class is, well - let's say interesting. What are the repercussions? The only thing that comes immediately to mind, is if you plan to use some sort of object persistence framework, its going to squish different instances that now look the same into one (perhaps).
Different objects with the same attribute data are going to be indistinguishable - which is why I hesitated before posting this answer. OTOH, its your class, its your app so, depending on the size/importance etc, it may be a fine way to do it.

If you don't like the buildin postcircumfix, provide your own.
class Foo { has $.bar };
my Foo $a .= new(:bar(1));
my %h{Foo} = $a => 'A', Foo.new(:bar(2)) => 'B';
multi sub postcircumfix:<{ }>(\SELF, WhateverCode $c) is raw {
gather for SELF.keys -> $k {
take $k if $c($k)
}
}
dd %h;
dd %h{* eqv $a};
OUTPUT
Hash[Any,Foo] %h = (my Any %{Foo} = (Foo.new(bar => 1)) => "A", (Foo.new(bar => 2)) => "B")
(Foo.new(bar => 1),).Seq

Related

IIFE alternatives in Raku

In Ruby I can group together some lines of code like so with a begin block:
x = begin
puts "Hi!"
a = 2
b = 3
a + b
end
puts x # 5
it's immediately evaluated and its value is the last value of the block (a + b here) (Javascripters do a similar thing with IIFEs)
What are the ways to do this in Raku? Is there anything smoother than:
my $x = ({
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
})();
say $x; # 5
Insert a do in front of the block. This tells Raku to:
Immediately do whatever follows the do on its right hand side;
Return the value to the do's left hand side:
my $x = do {
put "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
That said, one rarely needs to use do.
Instead, there are many other IIFE forms in Raku that just work naturally without fuss. I'll mention just two because they're used extensively in Raku code:
with whatever { .foo } else { .bar }
You might think I'm being silly, but those are two IIFEs. They form lexical scopes, have parameter lists, bind from arguments, the works. Loads of Raku constructs work like that.
In the above case where I haven't written an explicit parameter list, this isn't obvious. The fact that .foo is called on whatever if whatever is defined, and .bar is called on it if it isn't, is both implicit and due to the particular IIFE calling behavior of with.
See also if, while, given, and many, many more.
What's going on becomes more obvious if you introduce an explicit parameter list with ->:
for whatever -> $a, $b { say $a + $b }
That iterates whatever, binding two consecutive elements from it to $a and $b, until whatever is empty. If it has an odd number of elements, one might write:
for whatever -> $a, $b? { say $a + $b }
And so on.
Bottom line: a huge number of occurrences of {...} in Raku are IIFEs, even if they don't look like it. But if they're immediately after an =, Raku defaults to assuming you want to assign the lambda rather than immediately executing it, so you need to insert a do in that particular case.
Welcome to Raku!
my $x = BEGIN {
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
I guess the common ancestry of Raku and Ruby shows :-)
Also note that to create a constant, you can also use constant:
my constant $x = do {
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
If you can have a single statement, you can leave off the braces:
my $x = BEGIN 2 + 3;
or:
my constant $x = 2 + 3;
Regarding blocks: if they are in sink context (similar to "void" context in some languages), then they will execute just like that:
{
say "Executing block";
}
No need to explicitely call it: it will be called for you :-)

Raku Ambiguous call to infix(Hyper: Dan::Series, Int)

I am writing a model Series class (kinda like the one in pandas) - and it should be both Positional and Associative.
class Series does Positional does Iterable does Associative {
has Array $.data is required;
has Array $.index;
### Construction ###
method TWEAK {
# sort out data-index dependencies
$!index = gather {
my $i = 0;
for |$!data -> $d {
take ( $!index[$i++] => $d )
}
}.Array
}
### Output ###
method Str {
$!index
}
### Role Support ###
# Positional role support
# viz. https://docs.raku.org/type/Positional
method of {
Mu
}
method elems {
$!data.elems
}
method AT-POS( $p ) {
$!data[$p]
}
method EXISTS-POS( $p ) {
0 <= $p < $!data.elems ?? True !! False
}
# Iterable role support
# viz. https://docs.raku.org/type/Iterable
method iterator {
$!data.iterator
}
method flat {
$!data.flat
}
method lazy {
$!data.lazy
}
method hyper {
$!data.hyper
}
# Associative role support
# viz. https://docs.raku.org/type/Associative
method keyof {
Str(Any)
}
method AT-KEY( $k ) {
for |$!index -> $p {
return $p.value if $p.key ~~ $k
}
}
method EXISTS-KEY( $k ) {
for |$!index -> $p {
return True if $p.key ~~ $k
}
}
#`[ solution attempt #1 does NOT get called
multi method infix(Hyper: Series, Int) is default {
die "I was called"
}
#]
}
my $s = Series.new(data => [rand xx 5], index => [<a b c d e>]);
say ~$s;
say $s[2];
say $s<b>;
So far pretty darn cool.
I can go dd $s.hyper and get this
HyperSeq.new(configuration => HyperConfiguration.new(batch => 64, degree => 1))
BUT (there had to be a but coming), I want to be able to do hyper math on my Series' elements, something like:
say $s >>+>> 2;
But that yields:
Ambiguous call to 'infix(Hyper: Dan::Series, Int)'; these signatures all match:
(Hyper: Associative:D \left, \right, *%_)
(Hyper: Positional:D \left, \right, *%_)
in block <unit> at ./synopsis-dan.raku line 63
How can I tell my class Series not to offer the Associative hyper candidate...?
Note: edited example to be a runnable MRE per #raiph's comment ... I have thus left in the minimum requirements for the 3 roles in play per docs.raku.org
After some experimentation (and new directions to consider from the very helpful comments to this SO along the way), I think I have found a solution:
drop the does Associative role from the class declaration like this:
class Series does Positional does Iterable {...}
BUT
leave the Associative role support methods in the body of the class:
# Associative role support
# viz. https://docs.raku.org/type/Associative
method keyof {
Str(Any)
}
method AT-KEY( $k ) {
for |$!index -> $p {
return $p.value if $p.key ~~ $k
}
}
method EXISTS-KEY( $k ) {
for |$!index -> $p {
return True if $p.key ~~ $k
}
}
This gives me the Positional and Associative accessors, and functional hyper math operators:
my $s = Series.new(data => [rand xx 5], index => [<a b c d e>]);
say ~$s; #([a => 0.6137271559776396 b => 0.7942959887386045 c => 0.5768018697817604 d => 0.8964323560788711 e => 0.025740150933493577] , dtype: Num)
say $s[2]; #0.7942959887386045
say $s<b>; #0.5768018697817604
say $s >>+>> 2; #(2.6137271559776396 2.7942959887386047 2.5768018697817605 2.896432356078871 2.0257401509334936)
While this feels a bit thin (and probably lacks the full set of Associative functions) I am fairly confident that the basic methods will give me slimmed down access like a hash from a key capability that I seek. And it no longer creates the ambiguous call.
This solution may be cheating a bit in that I know the level of compromise that I will accept ;-).
Take #1
First, an MRE with an emphasis on the M1:
class foo does Positional does Associative { method of {} }
sub infix:<baz> (\l,\r) { say 'baz' }
foo.new >>baz>> 42;
yields:
Ambiguous call to 'infix(Hyper: foo, Int)'; these signatures all match:
(Hyper: Associative:D \left, \right, *%_)
(Hyper: Positional:D \left, \right, *%_)
in block <unit> at ./synopsis-dan.raku line 63
The error message shows it's A) a call to a method named infix with an invocant matching Hyper, and B) there are two methods that potentially match that call.
Given that there's no class Hyper in your MRE, these methods and the Hyper class must be either built-ins or internal details that are leaking out.
A search of the doc finds no such class. So Hyper is undocumented Given that the doc has fairly broad coverage these days, this suggests Hyper is an internal detail. But regardless, it looks like you can't solve your problem using official/documented features.
Hopefully this bad news is still better than none.2
Take #2
Where's the fun in letting little details like "not an official feature" stop us doing what we want to do?
There's a core.c module named Hyper.pm6 in the Rakudo source repo.
A few seconds browsing that, and clicks on its History and Blame, and I can instantly see it really is time for me to conclude this SO answer, with a recommendation for your next move.
To wit, I suggest you start another SO, using this answer as its heart (but reversing my presentation order, ie starting by mentioning Hyper, and that it's not doc'd), and namechecking Liz (per Hyper's History/Blame), with a link back to your Q here as its background. I'm pretty sure that will get you a good answer, or at least an authoritative one.
Footnotes
1 I also tried this:
class foo does Positional does Associative { method of {} }
sub postfix:<bar>(\arg) { say 'bar' }
foo.new>>bar;
but that worked (displayed bar).
2 If you didn't get to my Take #1 conclusion yourself, perhaps that was was because your MRE wasn't very M? If you did arrive at the same point (cf "solution attempt #1 does NOT get called" in your MRE) then please read and, for future SOs, take to heart, the wisdom of "Explain ... any difficulties that have prevented you from solving it yourself".

Determine if a variable in Raku is set

I intentionally avoid the term defined because a variable may very well have a defined value but the .defined method will return false (Failures, for instance).
Is there any way to determine whether a variable has had a value set to it?
my $foo;
say $foo; # (Any), its type object, no value assigned
my Str $bar;
say $bar; # (Str), its type object, no value assigned
my $abc = Str;
say $abc; # (Str), the type object we assigned to $abc
How can we disinguish $bar (no value set, typed as Str) from $abc (value set to Str)?
Given that $bar.WHICH == $abc.WHICH, but $bar.VAR.WHICH !== $abc.VAR.WHICH, and methods like .defined will return false for each, is there any quick and easy way to determine that there is a set value?
I supposed it could be checked against the default value, but then there'd be no way to distinguish between the value being by virtue of unset, versus by being set in code.
Variables are always set to some sort of value.
If you don't set it to a value, a value will be chosen for you.
Specifically it will be set to the default.
(If you don't choose a default, it will be set to the type object.)
my $abc;
say $abc.VAR.default.raku;
# Any
my Int $def = 42;
say $def.VAR.default.raku;
# Int
my $ghi is default(42) = 2;
say $ghi.VAR.default.raku;
# 42
What you're asking for isn't really something that Raku supports.
You could probably fake something close though.
(Every instance of Mu.new is unique.)
sub is-not-set ( Mu $_ is raw ) {
$_.self =:= $_.VAR.default
}
my $abc is default(Mu.new);
my $def is default(Mu.new) = Any;
my $ghi is default(Mu.new) = Mu.new;
say is-not-set $abc; # True
say is-not-set $def; # False
say is-not-set $ghi; # False
The thing is that assigning Nil will also set it to the default.
$def = Nil;
say is-not-set $def; # True
As will looking up the default and assigning it.
$ghi = $ghi.VAR.default;
say is-not-set $ghi; # True
I don't think you should worry about such things.
If you really really need something to happen the first time you assign to the variable, you could do something like this:
my $abc := Proxy.new(
# this FETCH only needs to return the default
# as this Proxy gets replaced upon the first assignment
FETCH => -> $ { Any },
STORE => -> $, $value {
# replace the Proxy with a new Scalar
$abc := do { my $abc = $value };
say 'first assignment just happened'
},
);
say $abc;
# Any
$abc = 1;
# first assignment just happened
say $abc;
# 1
$abc = 2;
say $abc;
# 2
The do block is just there so that $abc.VAR.name returns $abc.
Otherwise you could just write $abc := my $ = $value.
I think both the values are identical, but the containers have different type constraints.
Try
my Str $foo;
my $bar = Str;
use Test;
cmp-ok $bar, &[===], $foo, 'The values are identical';
isa-ok $bar, Str;
isa-ok $foo, Str;
isa-ok $bar.VAR.of, Mu;
nok $bar.VAR.of.isa(Str), 'The container $bar is not of Str' ;
isa-ok $foo.VAR.of, Str;
done-testing();
ok 1 - The values are identical
ok 2 - The object is-a 'Str'
ok 3 - The object is-a 'Str'
ok 4 - The object is-a 'Mu'
ok 5 - The container $bar is not of Str
ok 6 - The object is-a 'Str'
1..6
Is that a general question or an implementation problem? If the latter, maybe (ab)using roles is an option?
role isUnset {};
my Str $a = Str but isUnset;
say $a ~~ isUnset;
# meanwile
$a = 'set';
# ...
$a = Str;
# and then
say $a ~~ isUnset; # Now False
my Str $bar and my $bar = Str result in the same thing, both are of type Str but have no definite values. Str is a type object, not a value.
.defined would return True if you'd give $bar a definite value, such as "Str" (note the quotes surrounding the bareword).
You might try to assign a default value to a variable, instead of keeping it undefined:
my Str $bar is default("");
$bar will be Str only if it's assigned that value type; if its value is deleted via assigning Nil it will default again to the empty string. As a matter of fact, the default for a variable is its type object, so:
my Str $foo;
my $bar = Str;
say $foo eqv $bar
will, in fact, return True.

confusion about lists contained in an aggregate, maybe context problem?

Rakudo version 2020.01
I was writing some throw-away code and did not bother to implement a class, just used a Hash as work-alike. I found some surprising behaviour with lists.
class Q1 {}
class R1 {
has Str $.some-str is required;
has #.some-list is required;
}
my $r1 = R1.new(
some-str => '…',
some-list => (Q1.new, Q1.new, Q1.new)
);
# hash as poor man's class
my $r2 = {
some-str => '…',
some-list => (Q1.new, Q1.new, Q1.new)
};
multi sub frob(R1 $r1) {
for #`(Array) $r1.some-list -> $elem {
$elem.raku.say;
}
}
multi sub frob(Hash $r2) {
for #`(List) $r2<some-list> -> $elem {
$elem.raku.say;
}
}
frob $r1;
# OK.
# Q1.new
# Q1.new
# Q1.new
frob $r2;
# got:
# (Q1.new, Q1.new, Q1.new)
# expected:
# Q1.new
# Q1.new
# Q1.new
frob(Hash …) works as expected when I call .flat or .list on the list (even though it is already a list‽).
I tried to make a minimal test case, but this works identical AFAICT.
for [Q1.new, Q1.new, Q1.new] -> $elem {
$elem.raku.say;
}
for (Q1.new, Q1.new, Q1.new) -> $elem {
$elem.raku.say;
}
I have read the documentation on List and Scalar several times, but I still cannot make sense out of my observation. Why do I have to special treat the list in the Hash, but not in the class?
for doesn't loop over itemized values.
When you place something in a scalar container it gets itemized.
sub foo ( $v ) { # itemized
for $v { .say }
}
sub bar ( \v ) {
for v { .say }
}
foo (1,2,3);
# (1 2 3)
bar (1,2,3);
# 1
# 2
# 3
An element in a Hash is also a scalar container.
my %h = 'foo' => 'bar';
say %h<foo>.VAR.^name;
# Scalar
So if you place a list into a Hash, it will get itemized.
my %h;
my \list = (1,2,3);
%h<list> = list;
say list.VAR.^name;
# List
say %h<list>.VAR.^name;
# Scalar
So if you want to loop over the values you have to de-itemize it.
%h<list>[]
%h<list><>
%h<list>.list
%h<list>.self
#(%h<list>)
given %h<list> -> #list { … }
my #list := %h<list>;
(my # := %h<list>) # inline version of previous example
You could avoid this scalar container by binding instead.
%h<list> := list;
(This prevents the = operator from working on that hash element.)
If you noticed that in the class object you defined it with an # not $
class R1 {
has Str $.some-str is required;
has #.some-list is required;
}
If you changed it to an $ and mark it rw it will work like the Hash example
class R2 {
has Str $.some-str is required;
has List $.some-list is required is rw;
}
my $r2 = R2.new(
some-str => '…',
some-list => (1,2,3),
);
for $r2.some-list { .say }
# (1 2 3)
It has to be a $ variable or it won't be in a Scalar container.
It also has to be marked rw so that the accessor returns the actual Scalar container rather than the de-itemized value.
This has nothing to do with [] versus (). This has to do with the difference between $ (indicating an item) and % (indicating an Associative):
sub a(%h) { dd %h } # a sub taking an Associative
sub b(Hash $h) { dd $h } # a sub taking an item of type Hash
a { a => 42 }; # Hash % = {:a(42)}
b { a => 42 }; # ${:a(42)}
In the "b" case, what is received is an item. If you try to iterate over that, you will get 1 iteration, for that item. Whereas in the "a" case, you've indicated that it is something Associative that you want (with the % sigil).
Perhaps a clearer example:
my $a = (1,2,3);
for $a { dd $_ } # List $a = $(1, 2, 3)␤
Since $a is an item, you get one iteration. You can indicate that you want to iterate on the underlying thing, by adding .list:
for $a.list { dd $_ } # 1␤2␤3␤
Or, if you want to get more linenoisy, prefix a #:
for #$a { dd $_ } # 1␤2␤3␤
Not strictly an answer, but an observation: in Raku, it pays to use classes rather than hashes, contrary to Perl:
my %h = a => 42, b => 666;
for ^10000000 { my $a = %h<a> }
say now - INIT now; # 0.4434793
Using classes and objects:
class A { has $.a; has $.b }
my $h = A.new(a => 42, b => 666);
for ^10000000 { my $a = $h.a }
say now - INIT now; # 0.368659
Not only is using classes faster, it also prevents you from making typos in initialization if you add the is required trait:
class A { has $.a is required; has $.b is required }
A.new(a => 42, B => 666);
# The attribute '$!b' is required, but you did not provide a value for it.
And it prevents you from making typos when accessing it:
my $a = A.new(a => 42, b => 666);
$a.bb;
# No such method 'bb' for invocant of type 'A'. Did you mean 'b'?

Why does a Perl 6 Str do the Positional role, and how can I change []?

I'm playing around with a positional interface for strings. I'm aware of How can I slice a string like Python does in Perl 6?, but I was curious if I could make this thing work just for giggles.
I came up with this example. Reading positions is fine, but I don't know how to set up the multi to handle an assignment:
multi postcircumfix:<[ ]> ( Str:D $s, Int:D $n --> Str ) {
$s.substr: $n, 1
}
multi postcircumfix:<[ ]> ( Str:D $s, Range:D $r --> Str ) {
$s.substr: $r.min, $r.max - $r.min + 1
}
multi postcircumfix:<[ ]> ( Str:D $s, List:D $i --> List ) {
map( { $s.substr: $_, 1 }, #$i ).list
}
multi postcircumfix:<[ ]> ( Str:D $s, Int:D $n, *#a --> Str ) is rw {
put "Calling rw version";
}
my $string = 'The quick, purple butterfly';
{ # Works
my $single = $string[0];
say $single;
}
{ # Works
my $substring = $string[5..9];
say $substring;
}
{ # Works
my $substring = $string[1,3,5,7];
say $substring;
}
{ # NOPE!
$string[2] = 'Perl';
say $string;
}
The last one doesn't work:
T
uick,
(h u c)
Index out of range. Is: 2, should be in 0..0
in block <unit> at substring.p6 line 36
Actually thrown at:
in block <unit> at substring.p6 line 36
I didn't think it would work, though. I don't know what signature or traits it should have to do what I want to do.
Why does the [] operator work on a Str?
$ perl6
> "some string"[0]
some string
The docs mostly imply that the [] works on things that do the Positional roles and that those things are in list like things. From the [] docs in operators:
Universal interface for positional access to zero or more elements of a #container, a.k.a. "array indexing operator".
But a Str surprisingly does the necessary role even though it's not an #container (as far as I know):
> "some string".does( 'Positional' )
True
Is there a way to test that something is an #container?
Is there a way to get something to list all of its roles?
Now, knowing that a string can respond to the [], how can I figure out what signature will match that? I want to know the right signature to use to define my own version to write to this string through [].
One way to achieve this, is by augmenting the Str class, since you really only need to override the AT-POS method (which Str normally inherits from Any):
use MONKEY;
augment class Str {
method AT-POS($a) {
self.substr($a,1);
}
}
say "abcde"[3]; # d
say "abcde"[^3]; # (a b c)
More information can be found here: https://docs.raku.org/language/subscripts#Methods_to_implement_for_positional_subscripting
To make your rw version work correctly, you first need to make the Str which might get mutated also rw, and it needs to return something which in turn is also rw. For the specific case of strings, you could simply do:
multi postcircumfix:<[ ]> ( Str:D $s is rw, Int:D $i --> Str ) is rw {
return $s.substr-rw: $i, 1;
}
Quite often, you'll want an rw subroutine to return an instance of Proxy:
multi postcircumfix:<[ ]> ( Str:D $s is rw, Int:D $i --> Str ) is rw {
Proxy.new: FETCH => sub { $s.substr: $i },
STORE => sub -> $newval { $s.substr-rw( $i, 1 ) = $newval }
}
Although I haven't yet seen production code which uses it, there is also a return-rw operator, which you'll occasionally need instead of return.
sub identity( $x is rw ) is rw { return-rw $x }
identity( my $y ) = 42; # Works, $y is 42.
sub identity-fail( $x is rw ) is rw { return $x }
identity-fail( my $z ) = 42; # Fails: "Cannot assign to a readonly variable or a value"
If a function reaches the end without executing a return, return-rw or throwing an exception, the value of the last statement is returned, and (at present), this behaves as if it were preceded return-rw.
sub identity2( $x is rw ) is rw { $x }
identity2( my $w ) = 42; # Works, $w is 42.
There's a module that aims to let you do this:
https://github.com/zoffixznet/perl6-Pythonic-Str
However:
This module does not provide Str.AT-POS or make Str type do Positional or Iterable roles. The latter causes all sorts of fallout with core and non-core code due to inherent assumptions that Str type does not do those roles. What this means in plain English is you can only index your strings with [...] postcircumfix operator and can't willy-nilly treat them as lists of characters—simply call .comb if you need that.`