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

: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.

Related

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'?

Is there a 'clamp' method/sub for ranges/Num etc in Raku (i.e. Perl6)?

Is there a 'clamp' or equivalent method or sub in Perl6?
eg
my $range= (1.0 .. 9.9)
my $val=15.3;
my $clamped=$range.clamp($val);
# $clamped would be 9.9
$val= -1.3;
$clamped=$range.clamp($val);
# $clamped would be 1.0
Another tact you might like to explore is using a Proxy, which allows you to define "hooks" when fetching or storing a value from a container
sub limited-num(Range $range) is rw {
my ($min, $max) = $range.minmax;
my Numeric $store = $min;
Proxy.new(
FETCH => method () { $store },
STORE => method ($new) {
$store = max($min, min($max, $new));
}
)
}
# Note the use of binding operator `:=`
my $ln := limited-num(1.0 .. 9.9);
say $ln; # OUTPUT: 1
$ln += 4.2;
say $ln; # OUTPUT: 5.2
$ln += 100;
say $ln; # OUTPUT: 9.9
$ln -= 50;
say $ln; # OUTPUT: 1
$ln = 0;
say $ln; # OUTPUT: 1
This particular limited-num will initialise with it's min value, but you can also set it at declaration
my $ln1 := limited-num(1.0 .. 9.9) = 5.5;
say $ln1; # OUTPUT 5.5;
my $ln2 := limited-num(1.0 .. 9.9) = 1000;
say $ln2; # OUTPUT 9.9
I don't think so. So, perhaps:
multi clamp ($range, $value) {
given $range {
return .max when (($value cmp .max) === More);
return .min when (($value cmp .min) === Less);
}
return $value
}
my $range = (1.0 .. 9.9);
say $range.&clamp: 15.3; # 9.9
say $range.&clamp: -1.3; # 1
my $range = 'b'..'y';
say $range.&clamp: 'a'; # b
say $range.&clamp: 'z'; # y
The MOP allows direct exploration of the objects available in your P6 system. A particularly handy metamethod is .^methods which works on most built in objects:
say Range.^methods; # (new excludes-min excludes-max infinite is-int ...
By default this includes just the methods defined in the Range class, not the methods it inherits. (To get them all you could use say Range.^methods: :all. That'll net you a much bigger list.)
When I just tried it I found it also included a lot of methods unhelpfully named Method+{is-nodal}.new. So maybe use this instead:
say Range.^methods.grep: * !~~ / 'is-nodal' /;
This netted:
(new excludes-min excludes-max infinite is-int elems iterator
flat reverse first bounds int-bounds fmt ASSIGN-POS roll pick
Capture push append unshift prepend shift pop sum rand in-range
hyper lazy-if lazy item race of is-lazy WHICH Str ACCEPTS perl
Numeric min max BUILDALL)
That's what I used to lead me to my solution above; I sort of know the methods but use .^methods to remind me.
Another way to explore what's available is doc, eg the official doc's Range page. That netted me:
ACCEPTS min excludes-min max excludes-max bounds
infinite is-int int-bounds minmax elems list flat
pick roll sum reverse Capture rand
Comparing these two lists, sorted and bagged, out of curiosity:
say
<ACCEPTS ASSIGN-POS BUILDALL Capture Numeric Str WHICH append
bounds elems excludes-max excludes-min first flat fmt hyper
in-range infinite int-bounds is-int is-lazy item iterator
lazy lazy-if max min new of perl pick pop prepend push
race rand reverse roll shift sum unshift>.Bag
∩
<ACCEPTS Capture bounds elems excludes-max excludes-min flat
infinite int-bounds is-int list max min minmax pick
rand reverse roll sum>.Bag
displays:
Bag(ACCEPTS, Capture, bounds, elems, excludes-max, excludes-min,
flat, infinite, int-bounds, is-int, max, min, pick,
rand, reverse, roll, sum)
So for some reason, list, minmax, and sum are documented as Range methods but are not listed by my .^methods call. Presumably they're called Method+{is-nodal}.new. Hmm.
say Range.^lookup('minmax'); # Method+{is-nodal}.new
say Range.^lookup('minmax').name; # minmax
Yep. Hmm. So I could have written:
say Range.^methods>>.name.sort;
(ACCEPTS ASSIGN-POS AT-POS BUILDALL Bag BagHash Capture EXISTS-POS
Mix MixHash Numeric Set SetHash Str WHICH append bounds elems
excludes-max excludes-min first flat fmt hyper in-range infinite
int-bounds is-int is-lazy item iterator lazy lazy-if list max min
minmax new of perl pick pop prepend push race rand reverse roll
shift sum unshift)
Anyhow, hope that's helpful.
Strange that no one has suggested using augment. Admittedly, it creates global changes, but that might not be an issue.
augment class Range {
method clamp ($value) { ... }
}
You will need to use the pragmause MONKEY-TYPING in the same scope before the augment in order to use it though. But this way, you can simply say $range.clamp(5), for instance. It saves you one character over raiph's answer, but at the (not insignificant) cost of breaking precompilation.

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

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)

How to build lazy lists with defined generators and is there a "takeWhile" alternative?

I am reading through perl6intro on lazy lists and it leaves me confused about certain things.
Take this example:
sub foo($x) {
$x**2
}
my $alist = (1,2, &foo ... ^ * > 100);
will give me (1 2 4 16 256), it will square the same number until it exceeds 100. I want this to give me (1 4 9 16 25 .. ), so instead of squaring the same number, to advance a number x by 1 (or another given "step"), foo x, and so on.
Is it possible to achieve this in this specific case?
Another question I have on lazy lists is the following:
In Haskell, there is a takeWhile function, does something similar exist in Perl6?
I want this to give me (1 4 9 16 25 .. )
The easiest way to get that sequence, would be:
my #a = (1..*).map(* ** 2); # using a Whatever-expression
my #a = (1..*).map(&foo); # using your `foo` function
...or if you prefer to write it in a way that resembles a Haskell/Python list comprehension:
my #a = ($_ ** 2 for 1..*); # using an in-line expression
my #a = (foo $_ for 1..*); # using your `foo` function
While it is possible to go out of one's way to express this sequence via the ... operator (as Brad Gilbert's answer and raiph's answer demonstrate), it doesn't really make sense, as the purpose of that operator is to generate sequences where each element is derived from the previous element(s) using a consistent rule.
Use the best tool for each job:
If a sequence is easiest to express iteratively (e.g. Fibonacci sequence):
Use the ... operator.
If a sequence is easiest to express as a closed formula (e.g. sequence of squares):
Use map or for.
Here is how you could write a Perl 6 equivalent of Haskell's takewhile.
sub take-while ( &condition, Iterable \sequence ){
my \iterator = sequence.iterator;
my \generator = gather loop {
my \value = iterator.pull-one;
last if value =:= IterationEnd or !condition(value);
take value;
}
# should propagate the laziness of the sequence
sequence.is-lazy
?? generator.lazy
!! generator
}
I should probably also show an implementation of dropwhile.
sub drop-while ( &condition, Iterable \sequence ){
my \iterator = sequence.iterator;
GATHER: my \generator = gather {
# drop initial values
loop {
my \value = iterator.pull-one;
# if the iterator is out of values, stop everything
last GATHER if value =:= IterationEnd;
unless condition(value) {
# need to take this so it doesn't get lost
take value;
# continue onto next loop
last;
}
}
# take everything else
loop {
my \value = iterator.pull-one;
last if value =:= IterationEnd;
take value
}
}
sequence.is-lazy
?? generator.lazy
!! generator
}
These are only just-get-it-working examples.
It could be argued that these are worth adding as methods to lists/iterables.
You could (but probably shouldn't) implement these with the sequence generator syntax.
sub take-while ( &condition, Iterable \sequence ){
my \iterator = sequence.iterator;
my \generator = { iterator.pull-one } …^ { !condition $_ }
sequence.is-lazy ?? generator.lazy !! generator
}
sub drop-while ( &condition, Iterable \sequence ){
my \end-condition = sequence.is-lazy ?? * !! { False };
my \iterator = sequence.iterator;
my $first;
loop {
$first := iterator.pull-one;
last if $first =:= IterationEnd;
last unless condition($first);
}
# I could have shoved the loop above into a do block
# and placed it where 「$first」 is below
$first, { iterator.pull-one } … end-condition
}
If they were added to Perl 6/Rakudo, they would likely be implemented with Iterator classes.
( I might just go and add them. )
A direct implementation of what you are asking for is something like:
do {
my $x = 0;
{ (++$x)² } …^ * > 100
}
Which can be done with state variables:
{ ( ++(state $x = 0) )² } …^ * > 100
And a state variable that isn't used outside of declaring it doesn't need a name.
( A scalar variable starts out as an undefined Any, which becomes 0 in a numeric context )
{ (++( $ ))² } …^ * > 100
{ (++$)² } …^ * > 100
If you need to initialize the anonymous state variable, you can use the defined-or operator // combined with the equal meta-operator =.
{ (++( $ //= 5))² } …^ * > 100
In some simple cases you don't have to tell the sequence generator how to calculate the next values.
In such cases the ending condition can also be simplified.
say 1,2,4 ...^ 100
# (1 2 4 8 16 32 64)
The only other time you can safely simplify the ending condition is if you know that it will stop on the value.
say 1, { $_ * 2 } ... 64;
# (1 2 4 8 16 32 64)
say 1, { $_ * 2 } ... 3;
# (1 2 4 8 16 32 64 128 256 512 ...)
I want this to give me (1 4 9 16 25 .. )
my #alist = {(++$)²} ... Inf;
say #alist[^10]; # (1 4 9 16 25 36 49 64 81 100)
The {…} is an arbitrary block of code. It is invoked for each value of a sequence when used as the LHS of the ... sequence operator.
The (…)² evaluates to the square of the expression inside the parens. (I could have written (…) ** 2 to mean the same thing.)
The ++$ returns 1, 2, 3, 4, 5, 6 … by combining a pre-increment ++ (add one) with a $ variable.
In Haskell, there is a takeWhile function, does something similar exist in Perl6?
Replace the Inf from the above sequence with the desired end condition:
my #alist = {(++$)²} ... * > 70; # stop at step that goes past 70
say #alist; # [1 4 9 16 25 36 49 64 81]
my #alist = {(++$)²} ...^ * > 70; # stop at step before step past 70
say #alist; # [1 4 9 16 25 36 49 64]
Note how the ... and ...^ variants of the sequence operator provide the two variations on the stop condition. I note in your original question you have ... ^ * > 70, not ...^ * > 70. Because the ^ in the latter is detached from the ... it has a different meaning. See Brad's comment.

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