Self-modifying Loops
Loops. For, while, until, foreach. Perl has plenty of ways to repeat yourself.But what if you wanted to build a loop out of eval and regex? What if your code rewrote itself on every iteration?
Run it. Watch it count down from 10 to 1. No for loop. No while. Just a heredoc that eats itself.#!/usr/bin/env perl use qw|say|; $_ = <<'EOF'; 10; s~(\d+)(?{ say qq($1) })~$1-1~e; sleep 1; $1 ? eval : say q(Countdown complete!); EOF eval;
Part 1: THE SETUP
We're stuffing a chunk of Perl code into $ as a string. The single quotes around EOF mean no interpolation happens yet. It's just text.$_ = <<'EOF'; ...code... EOF
That text happens to be valid Perl. And we're about to run it.
Part 2: THE STARTING NUMBER
First line of our heredoc. Just a number sitting there.10;
In Perl, a bare number is a valid statement. It evaluates to itself and does nothing. But it's there in the string, waiting to be found by a regex.
Part 3: THE ENGINE
This is where it gets weird:Let's pull it apart:s~(\d+)(?{ say qq($1) })~$1-1~e;
So this regex:PIECE WHAT IT DOES ----------- -------------------------------------------- s~~~ Substitution operator (using ~ as delimiter) (\d+) Capture one or more digits into $1 (?{ ... }) **Embedded code block** - runs during the match say qq($1) Print the captured number $1-1 Replacement: the number minus one e Evaluate replacement as Perl code
The string in $ literally changes. After one pass:1. Finds the number (10 on first run) 2. Prints it (via the embedded code block) 3. Replaces it with itself minus one (10 becomes 9)
The code is rewriting itself.BEFORE: "10;\ns~..." AFTER: "9;\ns~..."
.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
Part 4: THE PAUSE
Slows it down so you can watch the countdown. Without this, it blasts through in milliseconds.sleep 1;
Part 5: THE LOOP CONDITION
This is a ternary operator doing the work of a while loop.$1 ? eval : say q(Countdown complete!);
When does $1 become falsy? When it hits zero.IF $1 IS TRUTHY THEN eval (run $_ again) IF $1 IS FALSY THEN print the goodbye message
No explicit loop counter. No increment. The regex IS the counter.10 -> truthy -> eval again 9 -> truthy -> eval again ... 1 -> truthy -> eval again 0 -> falsy -> print message and stop
Part 6: THE KICKOFF
Outside the heredoc, this single statement starts everything.eval;
eval with no argument evaluates $_. So it runs our heredoc code. That code modifies itself and calls eval again. Which runs the modified code. Which modifies itself and calls eval again.
Turtles all the way down.
Part 7: THE FULL WALKTHROUGH
Here's what happens step by step:The string transforms on every pass. The code literally changes between executions.ITERATION $_ CONTAINS $1 ACTION --------- ------------ --- ------------------------- 1 "10;..." 10 Print 10, replace with 9 2 "9;..." 9 Print 9, replace with 8 3 "8;..." 8 Print 8, replace with 7 4 "7;..." 7 Print 7, replace with 6 5 "6;..." 6 Print 6, replace with 5 6 "5;..." 5 Print 5, replace with 4 7 "4;..." 4 Print 4, replace with 3 8 "3;..." 3 Print 3, replace with 2 9 "2;..." 2 Print 2, replace with 1 10 "1;..." 1 Print 1, replace with 0 11 "0;..." 0 $1 is falsy, print goodbye
Part 8: THE ANNOTATED VERSION
#!/usr/bin/env perl use qw|say|; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Store code-as-data in $_ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $_ = <<'EOF'; 10; s~(\d+)(?{ say qq($1) })~$1-1~e; sleep 1; $1 ? eval : say q(Countdown complete!); EOF # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Single eval kicks off the chain # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # eval with no args runs $_ # The code in $_ modifies itself and calls eval again # This continues until $1 becomes 0 eval;
Part 9: WHY WOULD YOU DO THIS
You wouldn't. Not in production.But it demonstrates some powerful Perl concepts:
This is the kind of thing that makes Perl both loved and feared. The language doesn't stop you from doing weird stuff. It hands you a chainsaw and says "have fun."* **Code is data** (heredoc as executable string) * **Data is code** (eval turns strings into actions) * Regex can execute code (embedded code blocks) * Regex can do math (the /e modifier) * Self-modification is possible (and terrifying)
Part 10: VARIATIONS
Count up instead of down:Fibonacci sequence:$_ = <<'EOF'; 1; s~(\d+)(?{ say qq($1) })~$1+1~e; sleep 1; $1 < 10 ? eval : say q(Done!); EOF eval;
The pattern is always the same: code that rewrites itself, then evals itself, until some condition stops the recursion.$_ = <<'EOF'; 1 1; s~(\d+)\s+(\d+)(?{ say $2 })~$2 . q( ) . ($1+$2)~e; sleep 1; $2 < 100 ? eval : say q(Done!); EOF eval;
THE TAKEAWAY
Perl treats code and data as interchangeable. A string can become a program. A program can rewrite itself mid-execution. The regex engine can run arbitrary code while matching.
Is this practical? Rarely.
Is it powerful? Absolutely.
Is it Perl? Hell yes.
. /'\ / \ / \ / _ \ / (_) \ /___________\ | | | | _| |_ |_____| "Code that writes code"
Created By: Wildcard Wizard. Copyright 2026