Why does Perl 6's Map give me a value in one case and a list in another? - raku

I'm playing with Map and I get a result I don't understand.
First, I construct the Map. No big whoop:
> my $m = Map.new: '1' => :1st, '2' => :2nd;
Map.new(("1" => :st(1),"2" => :nd(2)))
I access a single element by the literal key and get back a Pair:
> $m<1>.^name
Pair
> $m<<1>>.^name
Pair
That's all fine.
If I try it with the key in a variable, I get back a List instead:
> my $n = 1
1
> $m<<$n>>.^name
List
That list has the right value, but why do I get a List in that case and not the $m<<1>> case?
And, once I have the list, I seem unable to chain another subscript to it:
> $m<<$n>>.[0]
===SORRY!=== Error while compiling:
Unable to parse quote-words subscript; couldn't find right double-angle quote
at line 2

When you access an associative value like this, the compiler can tell that it need only ever return one value.
$m< 1 >
$m<< 1 >>
In Perl 6, a singular value will in many cases behave just like a list of one value.
42.elems == 1 # True
42.[0] =:= 42 # True
In the following case, the compiler can't immediately tell that it will only produce one value:
my $n = 1;
$m<< $n >>;
As it could produce 2 values:
my $o = '1 2';
$m<< $o >>;
If you want the string to be a single key, you have to use quotation marks.
$m<< "$o" >>
Or use the more appropriate {}
$m{ $n }
The $m<1> is just a combination of two features.
Quotewords: ( qw<> and qqww<<>> )
< a b c > eqv ("a", "b", "c")
< "a b" c > eqv (「"a」, 「b"」, "c") # three strings
<< a b c >> eqv ("a", "b", "c")
<< "a b" c >> eqv ("a b", "c") # two strings
Associative indexing:
%h< a b c > eqv %h{ < a b c > }
%h<< "a b" c >> eqv %h{ << "a b" c >> }
Also I now get back different values.
$m< 1 >.WHAT =:= Pair
$m<< 1 >>.WHAT =:= Pair
$m<< $n >>.WHAT =:= Pair # different
$m<< $o >>.WHAT =:= List
The reason $m<<$n>>.[0] doesn't work is the compiler thinks you are using a hyper postfix >>.[0].
There are a couple ways of working around that.
Actually using a hyper postfix
$m<<$n>>>>.[0]
$m<<$n>>».[0]
Use an unspace. (can never be inside of an operator so will split them up)
$m<<$n>>\.[0]
$m<<$n>>\ .[0]
I think this is a bug, as it doesn't make much sense to be matching a hyper postfix inside of a quotewords statement.
(It doesn't affect $m<<1>>.elems)

Related

Why can't pass a manually created Pair to method without a slip?

:5hours is a Pair, hours => 5 is also a Pair:
> DateTime.now.truncated-to('day').later(:5hours)
2022-02-14T05:00:00+08:00
> :5hours.WHAT
(Pair)
> DateTime.now.truncated-to('day').later(hours => 5)
2022-02-14T05:00:00+08:00
> (hours => 5).WHAT
(Pair)
However, when I create a Pair manually, it doesn't match the signatures of later:
> DateTime.now.truncated-to('day').later(Pair.new('hours', 5))
Cannot resolve caller later(DateTime:D: Pair:D); none of these signatures match:
(Dateish:D: *%unit --> Dateish:D)
(Dateish:D: #pairs, *%_)
in block <unit> at <unknown file> line 1
But use a vertical before the Pair parameter is ok:
> DateTime.now.truncated-to('day').later(|Pair.new('hours', 5))
2022-02-14T05:00:00+08:00
So what's the difference between :5hours, Pair.new('hours', 5) and hours => 5? Why can't pass a manually created Pair such as Pair.new('hours', 5) to later method?
Aren't the following two the same thing, right?
> :5hours === Pair.new('hours', 5) === hours => 5
True
> :5hours eqv Pair.new('hours', 5) eqv hours => 5
True
> my $pair1 = Pair.new('hours', 5); dd $pair1; # Pair $pair1 = :hours(5)
> my $pair2 = :5hours; dd $pair2; # Pair $pair2 = :hours(5)
> my $pair3 = hours => 5; dd $pair3; # Pair $pair3 = :hours(5)
> my $pair4 = 'hours' => 5; dd $pair4; # Pair $pair4 = :hours(5)
Although :5hours and hours => 5 and :hours(5) and Pair.new(hours,5) and Pair.new(key => "hours", value => 5) are all different ways to create a Pair object, only the first three are syntactic sugar to indicate a named argument.
When you pass Pair.new("hours",5) as an argument, it is considered to be a Positional argument. Observe:
sub foo(*#_, *%_) {
dd #_, %_
}
foo hours => 5;
# []
# {:hours(5)}
foo Pair.new("hours",5);
# [:hours(5)]
# {}
As to why this is this way? Well, sometimes you want to pass a Pair as a positional argument. If a Pair was always considered to be a named argument, you wouldn't be able to do so.
As to why |Pair.new("hours",5) works as a named argument? The | in this context, flattens the given object (which is usually a Capture or a Hash/Map) into the arguments to the given subroutine. The Pair in this case, is seen as a degenerate case of a Map: an immutable Map with a single key / value. Observe:
foo |Pair.new("hours",5);
# []
# {:hours(5)}
Well, probably any Associative :-)
say Pair ~~ Associative; # True
.say for (:5hours).keys; # hours
.say for (:5hours).values; # 5
Finally, the | in this context is technically not a Slip, but syntactic sugar to flatten the given value into the arguments of a call.
The syntax predates the concept of a Slip (which was introduced in 2015 during the Great List Refactor). It was only very late in 2015 that | was OK'd by #Larry to also be used to indicate a Slip, as they conceptually do similar things.

ACCEPTS vs smartmatch in Hashes: what's the difference?

Theoretically, ~~ is syntactic sugar for ACCEPTS so the last two lines should return the same value:
my %myth-objects = %(Þor => "Mjólnir", Oðinn => "Hugin") ;
say %myth-objects;
say %myth-objects.ACCEPTS("Oðinn");
say %myth-objects ~~ "Oðinn";
However, the first returns True and the second returns False. What am I missing here?
There are two problems with what you have.
Smartmatch has two execution layers.
one is immediate
'abc' ~~ $_ eq 'XYZ'
.ACCEPTS is then called on the result, with the given value as the argument
do given 'abc' { ($_ eq 'XYZ').ACCEPTS($_) }
# ^ ^
# | |
# +------- 'abc'-------+
In the above case $_ eq 'XYZ' is False, and False.ACCEPTS(|) always returns False.
(Similarly True.ACCEPTS(|) always returns True.)
You could also return a Callable.
'abc' ~~ * eq 'XYZ'
This will appear to have the effect of removing the first immediate layer.
(It doesn't actually do that though.)
do given 'abc' { (* eq 'XYZ').ACCEPTS($_) }
do given 'abc' { (* eq 'XYZ').($_) }
do given 'abc' { $_ eq 'XYZ' }
Or it could return a type or literal.
'abc' ~~ ( 1 ?? Str !! Int ) # 'abc' ~~ Str
do given 'abc' { ( 1 ?? Str !! Int ).ACCEPTS($_) }
do given 'abc' { ( Str ).ACCEPTS($_) }
Str .ACCEPTS('abc')
You have the left hand side, and the right hand side swapped.
These two lines are similar.
(Ignoring that there are really two execution layers.)
'abc' ~~ 'XYZ'
'XYZ'.ACCEPTS('abc')
The important point to remember is that the right side of ~~ gets to decide how the smartmatch happens. The only way that can happen is if the method call was on it, not the left side.
(Note that all of the above also applies to when and where clauses. because they are also smartmatch features.)
So of course these have different results.
%myth-objects.ACCEPTS("Oðinn")
%myth-objects ~~ "Oðinn"
These three are similar.
%myth-objects ~~ "Oðinn"
do given %myth-objects { "Oðinn".ACCEPTS($_) } # identical
"Oðinn".ACCEPTS(%myth-objects) # simplified
As are these
%myth-objects.ACCEPTS("Oðinn")
do given "Oðinn" { %myth-objects.ACCEPTS($_) } # expanded to two layers
"Oðinn" ~~ %myth-objects # smartmatched
Is it not the other way:
say 'Oðinn' ~~ %myth-objects;
According to the doc:
The smartmatch operator aliases the left-hand side to $_ , then evaluates the right-hand side and calls .ACCEPTS($_) on it.

Can I delete an element of SetHash and use its value in Perl 6?

I would like to delete any element of a SetHash, so that its value be returned:
my SetHash $a;
$a<m n k> = 1..*;
my $elem = $a.mymethod;
say $elem; # n
say $a; # SetHash(m k)
I can do it in two steps as follows. Is there a better way?
my $elem = $a.pick;
$a{$elem}--;
And by the way, is there a more idiomatic way of adding several elements to a SetHash?
Is the following any better?
my SetHash $a;
$a{$_}++ for <m n k>;
or
my SetHash $a;
$a<m n k> X= True;
Since you want to remove any random element from the set and return the value you got, I'd recommend using the grab method on Setty things (only works properly on mutable sets like SetHash): https://docs.perl6.org/type/SetHash#(Setty)_method_grab
Depending on what exact return value you need, maybe grabpairs is better.
UPD I'm leaving this answer up for now rather than deleting it, but see timotimo's answer.
I would like to delete an element of a SetHash, so that its value be returned
Use the :delete adverb.
Per the Subscript doc page,
the :delete adverb will delete the element(s) from the collection or, if supported by the collection, create a hole at the given index(es), in addition to returning their value(s):
my %associative = << :a :b :c >> ;
my $deleted = %associative<< a c >> :delete ;
say $deleted ; # (True True)
say %associative ; # {b => True}
UPD Integrating #piojo's and #EugeneBarsky's comments and answers:
my %associative = << :a :b :c >> ;
my $deleted = .{.keys.pick } :delete :k with %associative ;
say $deleted ; # b
say %associative ; # {a => True, c => True}
is there a more idiomatic way of adding several elements to a SetHash?
Plain assignment with a list on the right hand side works, e.g.
$a<m n k> = True, True, True;
$a<m n k> = True xx *;
$a<m n k> = True ... *;
I've used and seen others using both the formulations in your examples too.
If I understand your comments correctly, you want to delete an element from a hash without knowing whether it exists, and get the element as a return value if it does exist. This can be done by combining the :delete and :k adverbs.
my %set := SetHash.new: ('a'..'g');
my #removed = %set<a e i o u>:delete :k;
say #removed; # output: [a e]
Another approach to deleting the element and returning it, is to augment the SetHash class with a custom method:
use v6;
use MONKEY-TYPING;
augment class SetHash {
method delete-elem(Any $elem) {
self.DELETE-KEY( $elem );
return $elem;
}
}
my SetHash $a = <m n k>.SetHash;
$a<a b c>»++; # add some more elements to the SetHash...
my $elem = $a.delete-elem( $a.pick );
say "Deleted: $elem";
say $a;
Output:
Deleted: m
SetHash(a b c k n)
Using a combination of the answers, it seems I've managed to do what I wanted:
my SetHash $s;
$s = <a b c d e f>.SetHash;
my $deleted = $s{ $s.pick } :delete :k;
say $deleted; # f
say $s; # SetHash(a b c d e)

inserting elements into List during Iteration in TCL/TK Scripting

I'm trying to add each input integer into a list and later sort it however I am having trouble adding each integer into the list during iteration.
code:
set l1 {1 2 3 4 5}
for {set i 0} {$i<[llength $l1]} {incr i} {
set data [gets stdin]
scan $data "%d" myint
if $myint<=0 {break} # stop if non positive number is found
set l1 {$myint} # supposed to add an input element into the list during iteration
}
puts $l1
Adding an element to the end of a list is easy; just use lappend instead of set:
lappend l1 $myint
When you come to sorting the list later, use lsort -integer, for example here with the puts:
puts [lsort -integer $l1]
(The lsort command works on values, not variables like lappend.)
However, it appears you're trying to actually input up to five values and sort those. If that's so, you'd be better off writing your code like this:
set l1 {}
for {set i 0} {$i < 5} {incr i} {
set data [gets stdin]
if {[eof stdin] || [scan $data "%d" myint] != 1 || $myint <= 0} {
break
}
lappend l1 $myint
}
puts [lsort -integer $l1]
The differences here? I'm using an empty initial list. I'm testing for End-Of-File. I'm checking the result of scan (in case someone supplies a non-integer). I'm using a compound expression. It's all little things, but they help the code be more robust.

Object Hash Lookup with `eqv`

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