This code returns nothing
<foo bar ber>.map: { $^a.comb.map: { $^b.say}};
It contains two nested maps, both of them in sink context. it shouldn't work, because a list sunk in a sink context is a no-op.
However, this works:
<foo bar ber>.map: *.say; # OUTPUT: «foobarber»
and it's again a list in sink context. So why does it work?
A map
does not return a List
, but rather a Seq
. A Seq
is a one-shot Iterable
sequence of values, and in sink context it will iterate its underlying iterator and discard the produced values. This is why a map
in sink context will iterate, but only one level deep. Add .flat
to sink even the inner values (by flattening them into a single top-level sequence):
<foo bar ber>.map({ $^a.comb.map: { $^b.say}}).flat # OUTPUT: «foobarber»
A List
would indeed not iterate its values in sink context, because a List
is a data structure that memorizes even lazily produced values so that they can be indexed repeatedly. Indeed, doing:
(<foo bar ber>.map: *.say).list;
Produces no output, because the Seq
was coerced into a List
, which does nothing in sink context.
Very few built-in operations on iterable data return a List
, since it would be a premature commitment to retain data. It's often useful to chain such operations together and have the data flow through them an item at a time, rather than having to be all held in memory at each intermediate step. This is what Seq
enables, and why so many things return it instead of List
.
I think this is because only the last statement of a map
is not sunk:
class A {
method sink() {
say "sunk"
}
}
<foo bar ber>.map: { A.new } # doesn't show 'sunk'
A.new; # shows 'sunk' once
<foo bar ber>.map: { A.new; 1 } # shows 'sunk' 3x
So the inner map
does not get sunk, and therefore doesn't run, because it's the sink-all
on the inner map (that gets called on the iterator by the sink
method) that makes things happen.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With