Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After first "once {next}" block, other same-scoped "once" blocks fail to execute

Tags:

raku

My original plan was to use two once {next} blocks to skip the first two lines in a file (here emulating a as a multiline string):

for "A\nB\nC\n".lines() -> $line {
    once {next}
    once {next}
    put $line;
}

But it only skipped one iteration instead of two, outputting the following:

B
C

Instead of what I expected:

C

Apparently a single once {next} somehow cancels all remaining once blocks in the same scope:

my $guard = 3;

loop {
    last if $guard-- <= 0;
    once { next };
    once { put 'A: once ' };
    once { put 'A: once again' };
    put 'A: many ';
}

$guard = 3;
loop {
    last if $guard-- <= 0;
    once { put 'B: once ' };
    once { next };
    once { put 'B: once again' };
    put 'B: many ';
}

$guard = 3;
loop {
    last if $guard-- <= 0;
    once { put 'C: once ' };
    once { put 'C: once again' };
    once { next };
    put 'C: many ';
}

Outputting:

A: many
A: many
B: once
B: many
B: many
C: once
C: once again
C: many
C: many

(Example code here is a modified version of code at https://docs.raku.org/language/control#once).

Is this a bug or am I misunderstanding once {next} ?

like image 555
Christopher Bottoms Avatar asked Jul 15 '20 22:07

Christopher Bottoms


1 Answers

The once construct semantics are associated with closure clones; since for is defined in terms of map, we can think of the block of the for loop being like a closure that is cloned once per loop, and that clone used for all iterations of the loop. The running of once blocks is done only on the first invocation of that closure clone. That is to say, it's a property at the level of the closure, not one of the once block itself.

The very same semantics apply to state variable initializers, which are defined in the same way (that is, they have once semantics). Therefore, this this also exhibits the same behavior:

for "A\nB\nC\n".lines() -> $line {
    state $throwaway-a = next;
    state $throwaway-b = next; # this `next` never runs
    put $line;
}

Alternative semantics could have been chosen, however a per-once (and so per-state variable) indicator would imply an extra piece of state is needed for each of them.

So far as the original problem goes, a clearer solution would be:

for "A\nB\nC\n".lines().skip(2) -> $line {
    put $line;
}
like image 187
Jonathan Worthington Avatar answered Oct 21 '22 14:10

Jonathan Worthington