Goto Loops
goto. The forbidden keyword. The thing your CS professor warned you about. The fast track to spaghetti code.But what if you stuck it inside an else block? What if you made it loop?
Run it. Watch it count from 7 to 10, then celebrate. No while. No for. Just goto doing things goto shouldn't do.#!/usr/bin/env perl use qw|say|; my $n = 7; if ($n == 10) { say qq|I see a ${n}!|; } else { R: say qq(Hmmm... looking for 10... \$n = ${\$n++}); sleep 1; $n > 10 ? say qq(Yay! It's a ${\--$n}!) : goto R; }
Part 1: THE SETUP
We're starting at 7. The script wants to find 10.my $n = 7;
The if statement checks if we're already there:
We're not at 10. So we fall into the else block. And that's where things get weird.if ($n == 10) { say qq|I see a ${n}!|; }
Part 2: THE LABEL
See that R: sitting right after the opening brace? That's a label.else { R:
Labels are landing pads for goto. You can put them almost anywhere. Inside an else block? Sure, why not. Perl doesn't care.
The label name is arbitrary. Could be LOOP: or AGAIN: or XYZZY: - Perl just needs something to jump to.
Part 3: THE INTERPOLATION TRICK
This line does two things at once. Let's dissect it:say qq(Hmmm... looking for 10... \$n = ${\$n++});
That last bit is dense:PIECE WHAT IT DOES ---------------- ------------------------------------------ qq(...) Double-quoted string (interpolates) \$n = Literal "$n =" (backslash escapes the $) ${\$n++} The magic part
So we:${ ... } Dereference whatever's inside \$n++ Reference to $n, then post-increment
The print shows the CURRENT value, then $n goes up by one.1. Take a reference to $n 2. Dereference it (getting current value) 3. Post-increment $n
Wait, 11? Yeah. We overshoot. Keep reading.First iteration: prints 7, $n becomes 8 Second iteration: prints 8, $n becomes 9 Third iteration: prints 9, $n becomes 10 Fourth iteration: prints 10, $n becomes 11
Part 4: THE DRAMATIC PAUSE
One second between iterations. So you can watch the countdown. Remove it if you're impatient.sleep 1;
Part 5: THE CONDITIONAL GOTO
A ternary operator deciding whether to celebrate or loop.$n > 10 ? say qq(Yay! It's a ${\--$n}!) : goto R;
If $n is greater than 10, we're done. Print and exit. If not, jump back to label R and do it all again.CONDITION THEN ELSE ----------- ------------------------------ -------- $n > 10 Print success message goto R
But notice the success message:
That's a PRE-decrement. $n was 11, becomes 10, we print 10.say qq(Yay! It's a ${\--$n}!)
The overshoot was intentional. We needed to go past 10 to trigger the exit condition, then back up one for the correct answer.
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
Part 6: THE FULL WALKTHROUGH
Four iterations through the else block. The goto creates a loop without any loop keywords.ITER $n START PRINTS $n END ACTION ---- -------- ------------------ ------ ----------- 1 7 "... $n = 7" 8 goto R 2 8 "... $n = 8" 9 goto R 3 9 "... $n = 9" 10 goto R 4 10 "... $n = 10" 11 $n > 10! - 11 "Yay! It's a 10!" 10 EXIT
Part 7: WHY THIS WORKS
Labels in Perl are just markers. They don't create scope. They don't affect control flow by themselves. They just sit there, waiting.goto jumps to a label unconditionally. Doesn't matter where the label is - same block, different block, inside a conditional. If Perl can see it, goto can reach it.
Putting a label at the start of an else block turns that block into a loop body. The goto at the end becomes the "continue" logic.
It's a while loop in disguise:
Same behavior. Different syntax. One will get you fired.# This goto version: else { R: do_stuff(); condition ? exit : goto R; } # Is equivalent to: else { while (!condition) { do_stuff(); } }
Part 8: THE ANNOTATED VERSION
#!/usr/bin/env perl use qw|say|; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Starting value # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ my $n = 7; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # The fake "loop" # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ($n == 10) { # Already at target - just print say qq|I see a ${n}!|; } else { R: # <-- Label: goto target # Print current value, THEN increment # ${\$n++} dereferences and post-increments say qq(Hmmm... looking for 10... \$n = ${\$n++}); # Dramatic pause sleep 1; # Exit condition with pre-decrement to correct overshoot # If not > 10, jump back to R and repeat $n > 10 ? say qq(Yay! It's a ${\--$n}!) : goto R; }
Part 9: GOTO FLAVORS
Perl actually has three kinds of goto:The label form is the "classic" goto that everyone warns about.goto LABEL Jump to a label (what we used) goto &NAME Replace current sub with another (tail call) goto EXPR Computed goto (jump to dynamic label)
The &NAME form is actually useful - it's a proper tail call that doesn't grow the call stack. Some recursive algorithms need it.
The EXPR form is terrifying. You can compute where to jump at runtime. Don't.
Part 10: WHEN GOTO ISN'T EVIL
"Never use goto" is too strong. There are legitimate uses:But using goto to build a loop inside an else block? That's showing off. Save it for code golf and late-night Perl hacking sessions.* Breaking out of deeply nested loops (though labeled loops work) * Tail call optimization with goto &subname * Generated code (parsers, state machines) * Cleanup sections (C-style error handling)
The real lesson here isn't "use goto." It's "Perl lets you put labels anywhere, and that's both powerful and dangerous."
_____ / \ | GOTO | | ---> | \_____/ | | ___V___ / \ | LABEL: | \_______/ "The forbidden jump" - use responsibly
Created By: Wildcard Wizard. Copyright 2026