Why Repeat Instead of Regular Loops
Most programming languages treat loops as different language features.
You learn for loops. Then while loops. Then do while loops. Then foreach loops. Then for...in, for...of, iterator helpers, generator pipelines, and whatever collection APIs your language has accumulated over the years. Each one solves a slightly different problem, uses slightly different syntax, and follows slightly different rules.
Goblin takes a different view.
Goblin does not believe these are fundamentally different operations. Whether you are counting to ten, processing every item in a collection, waiting for a condition to change, or running forever until something stops you, you are still doing the same thing: repeating.
The details change.
The repetition does not.
That observation is the foundation of Goblin's loop design.
The Problem with Traditional Loops
Most languages organize loops around implementation details. Before you can express your intent, you must decide what kind of loop you are writing. Is this a for loop? A while loop? A foreach loop? A range loop? A counting loop? A loop that runs at least once?
Those distinctions make sense from the perspective of language implementation, but they are not usually how programmers think about problems.
When a programmer sits down to write code, they rarely think:
I need a foreach loop.
They think:
I need to process every item.
They do not think:
I need a while loop.
They think:
Keep doing this until something changes.
The loop keyword becomes an extra decision that exists between the programmer and the problem they are trying to solve.
Goblin removes that decision.
Repeat Is the Loop
Goblin has exactly one loop keyword:
repeat
repeat
Everything else is determined by what follows it.
Instead of choosing a loop type, you describe the thing being repeated and Goblin determines the repetition behavior automatically.
This means you only learn one repetition model. You do not need separate mental models for counting loops, conditional loops, collection loops, range loops, and infinite loops.
You learn repeat.
Then you learn what you can repeat.
The Input Chooses the Loop
The value after repeat determines how repetition works.
A bare repeat means repeat forever.
repeat tick() xx
repeat tick() xx
A number means repeat a fixed number of times.
repeat 10 :say("digging...") xx
repeat 10 :say("digging...") xx
A condition means repeat while the condition remains true.
repeat health > 0 health |= health - 10 xx
repeat health > 0 health |= health - 10 xx
A collection means repeat for each item.
repeat loot :say(it) xx
repeat loot :say(it) xx
A range means repeat across the values in that range.
repeat 1...10 :say(it) xx
repeat 1...10 :say(it) xx
The loop keyword never changes because the operation never changes. The only thing that changes is the source of repetition.
That is the core idea behind Goblin's loop system.
Why Goblin Does Not Have While Loops
A traditional while loop means:
Continue repeating while this condition remains true.
Goblin already does that.
repeat health > 0 health |= health - 10 xx
repeat health > 0 health |= health - 10 xx
The condition itself tells Goblin everything it needs to know. Introducing a separate while keyword would create additional vocabulary without creating additional capability.
The condition is already present.
The repetition is already present.
Nothing else is needed.
Why Goblin Does Not Have For Loops
Most for loops exist to repeat a fixed number of times.
Goblin already does that.
repeat 5 :say("working...") xx
repeat 5 :say("working...") xx
If you need to know which iteration is currently executing, Goblin automatically provides idx.
repeat 5 :say("step {idx}") xx
repeat 5 :say("step {idx}") xx
There is no counter variable to create. There is no increment operation to remember. There is no bounds check to maintain. The programmer expresses intent and Goblin handles the mechanics.
This pattern appears throughout the language.
The programmer describes what should happen.
Goblin handles how it happens.
Why Goblin Does Not Have Foreach Loops
A foreach loop means:
Process every item in a collection.
Goblin already does that.
names | ["Dan", "Lisa", "Carl"] repeat names :say("Hello, {it}") xx
names | ["Dan", "Lisa", "Carl"] repeat names :say("Hello, {it}") xx
The collection itself tells Goblin how repetition should behave. The loop keyword does not need to change simply because the input changed.
This is one of the central themes of Goblin's design. Different inputs should not require different vocabulary when the underlying operation remains the same.
You are still repeating.
Loop Variables
When repeating over collections, Goblin automatically provides useful values inside the loop body.
loot | ["sword", "shield", "potion"] repeat loot :say("{idx}: {it}") xx
loot | ["sword", "shield", "potion"] repeat loot :say("{idx}: {it}") xx
Output:
0: sword 1: shield 2: potion
0: sword 1: shield 2: potion
The available variables depend on what is being repeated.
| Loop Type | Variables |
|---|---|
| Count Loop | idx |
| Array Loop | it, idx |
| String Loop | it, idx |
| Range Loop | it, idx |
| Map Loop | key, val |
If you prefer a more descriptive name than it, you can provide one using as.
repeat loot as item :say(item) xx
repeat loot as item :say(item) xx
This replaces it with your chosen name while leaving idx available.
The goal is convenience without sacrificing readability.
Why Goblin Does Not Have Do-While Loops
A do-while loop exists because some problems need to run at least once before deciding whether repetition should continue.
Goblin already supports that pattern.
repeat command | :read_line() stop if: command == "quit" xx
repeat command | :read_line() stop if: command == "quit" xx
The loop always executes at least once because there is no condition before execution begins.
More importantly, the stopping rule appears exactly where the reader expects to find it: inside the loop body, next to the logic that determines whether repetition should continue.
The result is often easier to read than a separate do while construct.
Loops Are Repetition Plus Control
Once you strip away the vocabulary, most loop features reduce to a surprisingly small set of operations.
You need a way to repeat.
Occasionally you need to skip the current iteration.
Sometimes you need to stop completely.
Sometimes you need to move forward through a collection without visiting every item.
Goblin names those operations directly.
skip stop jump
skip stop jump
skip ignores the rest of the current iteration.
stop ends the loop.
jump moves the loop cursor forward.
Together these cover the same ground as continue, break, step_by, scan-forward loops, and a large amount of manual index management.
Rather than creating additional loop forms, Goblin provides a small set of control operations that work consistently across repetition.
Judge Works Naturally with Repeat
One of the consequences of Goblin's design is that looping and decision making remain separate concerns.
Many languages encourage deeply nested conditionals inside loops. Goblin already has a dedicated construct for expressing decisions: judge.
repeat users judge it{"banned"}: skip it{"deleted"}: skip else: process_user(it) xx xx
repeat users judge it{"banned"}: skip it{"deleted"}: skip else: process_user(it) xx xx
The loop is responsible for repetition.
The judge is responsible for decision making.
Each construct does one job.
The result is often easier to read than a chain of nested if statements because the control flow becomes explicit. The loop repeats. The judge decides.
judge_all works naturally as well.
repeat notifications judge_all it{"urgent"}: send_alert(it) it{"email"}: send_email(it) it{"audit"}: write_log(it) xx xx
repeat notifications judge_all it{"urgent"}: send_alert(it) it{"email"}: send_email(it) it{"audit"}: write_log(it) xx xx
The loop controls repetition. The judge controls decisions. The two concepts reinforce each other rather than competing with each other.
Filtering
Many languages introduce special filtering syntax, iterator pipelines, or helper functions simply to skip unwanted items during iteration.
Goblin uses skip.
repeat posts skip if: it{"draft"} skip if: it{"archived"} publish(it) xx
repeat posts skip if: it{"draft"} skip if: it{"archived"} publish(it) xx
This reads like a series of guards.
If the post is a draft, skip it.
If the post is archived, skip it.
Otherwise, process it.
Filtering does not require a special loop form because filtering is simply loop control.
Jumping Through a Loop
Some loops do not need every item.
Sometimes you want to move ahead.
Sometimes you want to scan forward until something important appears.
Other languages often solve these problems with manual indexes, nested loops, iterator adapters, or specialized traversal APIs. Goblin uses jump.
repeat items jump 2 :say(it) xx
repeat items jump 2 :say(it) xx
jump moves the loop cursor forward.
You can also jump until a condition becomes true.
repeat tokens if it{"type"} == "section_start" jump until it{"type"} == "section_end" xx xx
repeat tokens if it{"type"} == "section_start" jump until it{"type"} == "section_end" xx xx
This allows the programmer to express intent directly. Move ahead. Find the thing. Continue processing.
The loop remains a repeat.
Only the cursor changes.
The Design Rule
Goblin does not remove loop power.
It removes loop vocabulary.
Other languages provide different keywords for different loop shapes. Goblin keeps one repetition construct and lets the input determine the behavior.
repeat /// forever repeat 10 /// count repeat health > 0 /// condition repeat items /// collection repeat stats /// map repeat 1...10 /// range
repeat /// forever repeat 10 /// count repeat health > 0 /// condition repeat items /// collection repeat stats /// map repeat 1...10 /// range
Then loop control handles the rest.
skip /// next iteration stop /// end loop jump /// move cursor
skip /// next iteration stop /// end loop jump /// move cursor
This is not fewer features.
It is the same collection of loop behaviors expressed through a smaller and more consistent mental model.
Why This Matters
Loops are one of the first things programmers learn, but they are also one of the first places where languages expose implementation details. Counters, indexes, bounds checks, iterators, enumerators, cursor management, and loop variants all become things the programmer must think about before they can express the actual task.
Goblin attempts to reverse that relationship.
If you want to do something again, write repeat.
If you want to ignore the current iteration, write skip.
If you want to stop, write stop.
If you want to move ahead, write jump.
That is the hill.
Loops are repetition plus control.
Goblin says that directly.