Jump anywhere

Label-based jumps

    outer-loop: loop {
        inner-loop: loop {
            # NYI # goto inner-loop if rand > 0.5; # Hard goto
            next inner-loop if rand > 0.5; # Next loop iteration
            redo inner-loop if rand > 0.5; # Re-execute block
            last outer-loop if rand > 0.5; # Exit the loop
            ENTER { say "Entered inner loop block" }
            LEAVE { say "Leaving inner loop block" }
        }
        ENTER { say "Entered outer loop block" }
        LEAVE { say "Leaving outer loop block" }
        LAST  { say "Ending outer loop" }
    }

Produces random output, but here's a representative run:

Output:

 Entered outer loop block
 Entered inner loop block
 Leaving inner loop block
 Entered inner loop block
 Leaving inner loop block
 Entered inner loop block
 Leaving inner loop block
 Leaving outer loop block
 Ending outer loop

Continuation-based execution

Continuations in Perl 6 are currently limited to use in generators via the gather/take model:

    my @list = lazy gather for ^100 -> $i {
        if $i.is-prime {
            say "Taking prime $i";
            take $i;
        }
    }

    say @list[5];

This outputs:

Output:

 Taking prime 2
 Taking prime 3
 Taking prime 5
 Taking prime 7
 Taking prime 11
 Taking prime 13
 13

Notice that no further execution of the loop occurs. If we then asked for the element at index 20, we would expect to see 15 more lines of "Taking prime..." followed by the result: 73.

Failures and exceptions

Exceptions are fairly typical in Perl6:

    die "This is a generic, untyped exception";

Will walk up the stack until either some `CATCH` block intercepts the specific exception type or we exit the program.

But if a failure should be recoverable (e.g. execution might reasonably continue along another path) a failure is often the right choice. The fail operator is like "return", but the returned value will only be valid in boolean context or for testing definedness. Any other operation will produce the original exception with the original exception's execution context (e.g. traceback) along with the current context.

    sub foo() { fail "oops" }
    my $failure = foo;
    say "Called foo";
    say "foo not true" unless $failure;
    say "foo not defined" unless $failure.defined;
    say "incremented foo" if $failure++; # exception

Produces:

Output:

 Called foo
 foo not true
 foo not defined
 oops
   in sub foo at fail.p6 line 1
   in block <unit> at fail.p6 line 2
 Actually thrown at:
   in any  at gen/moar/m-Metamodel.nqp line 3090
   in block <unit> at fail.p6 line 6

However, an exception can `.resume` in order to jump back to the failure point (this is why the stack is not unwound until after exception handling).

  sub foo($i) {
      if $i == 0 {
          die "Are you sure you want /0?";
      }
      say "Dividing by $i";
      1/$i.Num + 0; # Fighting hard to make this fail
  }

  for ^10 -> $n {
      my $recip = foo($n);
      say "1/{$n} = {$recip.perl}";
  }

  CATCH {
      when ~$_ ~~ m:s/Are you sure/ { .resume; #`(yes, I'm sure) }
  }

This code raises an exception on a zero input, but then resumes execution, divides be zero and then raises a divide by zero exception which is not caught:

Output:

 Dividing by 0
 Attempt to divide 1 by zero using /
   in sub foo at fail.p6 line 6
   in block <unit> at fail.p6 line 10

 Actually thrown at:
   in sub foo at fail.p6 line 6
   in block <unit> at fail.p6 line 10