• Proposal: 20250806-loop-range
• Authors: Wylan Shoemaker
• Status: Draft
• Discussion: (link to GitHub PR or thread)
⸻
Summary
This RFC proposes the introduction of a new loop construct in Loom that supports:
• Iteration over numeric ranges using .. and ..=
• Optional step value for custom increments/decrements
• An optional rev prefix for reversed iteration
The goal is to create a more readable, modern alternative to C-style loops, while also providing powerful, safe iteration over integers and floats.
⸻
Motivation
The current looping mechanisms in most languages either:
• Require verbose, error-prone syntax (for (;;)),
• Or are overly restrictive when it comes to range flexibility.
We want to design a looping construct that:
• Reads naturally and clearly,
• Supports both inclusive/exclusive ranges,
• Works for both integers and floats (with explicit step for the latter),
• Allows reverse iteration in a clean and intuitive way,
• Can eventually extend to support pattern destructuring and custom iterators.
This will make Loom more expressive and accessible, particularly for mathematical or data-centric code.
⸻
Guide-Level Explanation
Basic Syntax
loop (i in 0..5) {
print(i); // 0, 1, 2, 3, 4
}
loop (i in 0..=5) {
print(i); // 0, 1, 2, 3, 4, 5
}
With Step
loop (i in 0..10 step 2) {
print(i); // 0, 2, 4, 6, 8
}
Reversed Loop
rev loop (i in 0..5) {
print(i); // 4, 3, 2, 1, 0
}
Float Ranges
loop (f in 0.0..1.0 step 0.2) {
print(f); // 0.0, 0.2, 0.4, 0.6, 0.8
}
-
- step is required for floating-point ranges.
⸻
| Edge |
Cases |
| Case |
Behavior |
| loop (i in 5..5) |
0 iterations |
| loop (i in 5..5 step 1) |
0 iterations |
| loop (i in 5..0) |
Infinite loop (unless step) |
| loop (f in 0.0..1.0) |
Compile-time error (no step) |
⸻
Reference-Level Explanation
Grammar Changes
LoopStmt ::= [ 'rev' ] 'loop' '(' Pattern 'in' RangeExpr [ 'step' Expr ] ')' Block
RangeExpr ::= Expr '..' Expr | Expr '..=' Expr
Pattern ::= Identifier | '(' Identifier (',' Identifier)* ')'
Type System Rules
• RangeExpr must resolve to two values of matching type.
• Allowed types:
• int (default step = 1)
• float (requires step)
• If range is float and step is not provided → compile-time error
Codegen Considerations
For integer ranges:
start = a
end = b
inclusive = ..=
step = 1 (default)
direction = forward (unless `rev` or step < 0)
For float ranges:
steps = ceil((end - start) / step)
loop: i = start + step * n
Ensure float step multiplication uses n * step and clamps iteration to avoid floating point drift (optional precision safeguard).
⸻
Drawbacks
• Adds complexity to the language’s loop model.
• Requires runtime or compile-time handling of float drift.
• Potential confusion between rev loop and step -N unless clarified.
• Cannot fully prevent float precision issues in user logic.
⸻
Alternatives
Symbol-based (~, !)
Previously considered symbolic options like:
loop (i := 0 -> 10 ~2 !) { }
These were rejected due to:
• Difficulty typing
• Lower readability
• Poor accessibility for new users
Reusing for (...)
We opted for loop to:
• Avoid confusion with for-each semantics in other languages
• Give Loom its own identity
⸻
Prior Art
• Rust: Uses for x in 0..10, rev, step_by, etc.
• Swift: Supports stride(from:to:by:)
• Python: Uses range(start, stop, step)
• Zig: Uses for (...) |i| with ranges
We draw heavily from Rust, but adapt to Loom’s more flexible and expressive syntax model.
⸻
Unresolved Questions
• Should rev be allowed for float ranges?
• Should rev imply step -1 for int ranges (if step omitted)?
• Should we allow custom iterator objects like range(0, 10, 2)?
• How to best avoid or warn about float drift?
⸻
Future Directions
• Support loop ((k, v) in map) with destructuring
• Support iterators (loop (x in someIterator))
• Add range filters or conditions (loop (i in 0..10 if i % 2 == 0))
• Allow loop labels and break/continue targeting
• Proposal: 20250806-loop-range
• Authors: Wylan Shoemaker
• Status: Draft
• Discussion: (link to GitHub PR or thread)
⸻
Summary
This RFC proposes the introduction of a new loop construct in Loom that supports:
• Iteration over numeric ranges using .. and ..=
• Optional step value for custom increments/decrements
• An optional rev prefix for reversed iteration
The goal is to create a more readable, modern alternative to C-style loops, while also providing powerful, safe iteration over integers and floats.
⸻
Motivation
The current looping mechanisms in most languages either:
• Require verbose, error-prone syntax (for (;;)),
• Or are overly restrictive when it comes to range flexibility.
We want to design a looping construct that:
• Reads naturally and clearly,
• Supports both inclusive/exclusive ranges,
• Works for both integers and floats (with explicit step for the latter),
• Allows reverse iteration in a clean and intuitive way,
• Can eventually extend to support pattern destructuring and custom iterators.
This will make Loom more expressive and accessible, particularly for mathematical or data-centric code.
⸻
Guide-Level Explanation
Basic Syntax
With Step
Reversed Loop
Float Ranges
⸻
⸻
Reference-Level Explanation
Grammar Changes
Type System Rules
• RangeExpr must resolve to two values of matching type.
• Allowed types:
• int (default step = 1)
• float (requires step)
• If range is float and step is not provided → compile-time error
Codegen Considerations
For integer ranges:
For float ranges:
Ensure float step multiplication uses n * step and clamps iteration to avoid floating point drift (optional precision safeguard).
⸻
Drawbacks
• Adds complexity to the language’s loop model.
• Requires runtime or compile-time handling of float drift.
• Potential confusion between rev loop and step -N unless clarified.
• Cannot fully prevent float precision issues in user logic.
⸻
Alternatives
Symbol-based (~, !)
Previously considered symbolic options like:
These were rejected due to:
• Difficulty typing
• Lower readability
• Poor accessibility for new users
Reusing for (...)
We opted for loop to:
• Avoid confusion with for-each semantics in other languages
• Give Loom its own identity
⸻
Prior Art
• Rust: Uses for x in 0..10, rev, step_by, etc.
• Swift: Supports stride(from:to:by:)
• Python: Uses range(start, stop, step)
• Zig: Uses for (...) |i| with ranges
We draw heavily from Rust, but adapt to Loom’s more flexible and expressive syntax model.
⸻
Unresolved Questions
• Should rev be allowed for float ranges?
• Should rev imply step -1 for int ranges (if step omitted)?
• Should we allow custom iterator objects like range(0, 10, 2)?
• How to best avoid or warn about float drift?
⸻
Future Directions
• Support loop ((k, v) in map) with destructuring
• Support iterators (loop (x in someIterator))
• Add range filters or conditions (loop (i in 0..10 if i % 2 == 0))
• Allow loop labels and break/continue targeting