Feature Request: for-else
in Verse
Currently, handling failed iterations or filtering in Verse often requires verbose if/else
logic. This proposal introduces a cleaner and more idiomatic for-else
control structure.
Proposal
Introduce support for an optional else
block in verse for
loop syntax. This else
block would be executed:
- Never: if the loop has no failable expressions, or the expressions never fails;
- Once: if the loop fails to “initialize” (e.g., an initial failure occurs when trying to retrieve collection of items to iterate);
- During iteration: for elements that fail a conditional filter (based on already existing
for
loop failure context), if specified.
Why This Is Useful
Verse already has built-in success/failure semantics, which makes the for-else
concept even more natural and powerful.
- Note: Other languages (like Python) already have
for-else
syntax, but they behaves differently than this proposal. This proposal has a specific behavior and powerful usage that is related to how verse can handle failure checks.
Key Use Cases
-
Fallback for failed iterations
Cleanly detect when there are no valid elements to iterate over, without needing to enclose thefor
loop insideif
statements to handle these cases; -
Per-element failure handling
Apply fallback logic when specific elements fail a condition (IsActive[]
, etc.) without manualif/else
per element inside the loop. -
Reduces boilerplate and improves clarity
Avoids deeply nested conditionals and keeps failure logic unified, while still being readable and coherent.
Current Methods/Workarounds
I will show some examples of current ways to handle some scenarios. Later I will provide the suggested for-else
alternative to account instead of using these methods:
Example 1: Normal Loop (basic example of always-succeeding iteration)
for (X := 0..100) {
# Do Stuff
}
Example 2: Filtered Loop (basic example of filtering elements during iteration)
MyValues := for (Player : AvailablePlayers, Player.IsActive[]) {
Player.GetScore()
}
Example 3: Handling failed iteration initialization with if
MyValues := if (ValidPlayers := GetValidPlayers[]) {
for (Player : ValidPlayers) {
# Do Stuff
}
} else {
Print("No valid player found")
array{}
}
Example 4: Filtering inside the loop manually
for (Player : AvailablePlayers) {
if (Player.IsActive[]) {
# Do Stuff
} else {
Print("Player is not valid")
}
}
Example 5: Handling failed element condition check with a fallback
MyValues := for (Player : AvailablePlayers) {
if (Player.IsActive[]) {
Player.GetScore()
} else {
0 # Assign Default
}
}
Example 6: Handling failed element condition check with a fallback and/or with filtering behavior
MyValues := for (Player : AvailablePlayers) {
if (Player.IsActive[]) {
Player.GetScore()
} else {
StuffResult := DoStuff()
if (StuffResult?) {
0 # Assign Default if condition pass
} else {
continue # Skip current item to not be on the MyValues list if condition fails
}
}
}
Proposed for-else
Syntax
Now, comparing to the 6 examples above, here is how it would be when using the proposed for-else
syntax:
Example 1 (Compared to Normal and Basic for
loop usage)
for (X := 0..100) {
# Do Stuff
} else {
Print("Unreachable") # This will never fire since iteration will never fail
}
Example 2 (Compared to Filtering behavior of for
loop failable contexts)
MyValues := for (Player : AvailablePlayers, Player.IsActive[]) {
Player.GetScore()
} # Filter from result by not having the else block
Example 3: Using for-else
on iteration that fails initialization is unreachable, returning an empty array
MyValues := for (Player : GetValidPlayers[]) {
# Do Stuff
} else {
Print("Unreachable")
# This will never fire due to failing
# the iteration condition itself
}
(Note that the proposed syntax is not needed or used on these first three examples above, but I still let them here just to keep the proposal description clear and consistent)
Example 4: Usage of for-else
to handle per-element fallbacks
for (Player : AvailablePlayers, Player.IsActive[]) {
# Do Stuff
} else {
Print("Player is not valid")
# This will only be fired on inactive players (fail IsActive[])
# Can be fired multiple times during the iteration (one for each "Player")
# Current "Player" value from iteration is acessible since it's valid
}
Example 5: Usage of for-else
to handle per-element fallbacks and assign a default
MyValues := for (Player : AvailablePlayers, Player.IsActive[]) {
Player.GetScore()
} else {
0 # Assign default
}
Example 6: Usage of for-else
to handle per-element fallbacks, assigning defaults or more complex filtering logic
MyValues := for (Player : AvailablePlayers, Player.IsActive[]) {
Player.GetScore()
} else {
if (SomeCondition?). 0 # Assign Default if condition pass
else. continue # Skip current item to not be on the MyValues list if condition fails
}
Common Objections & Counterpoints
“We can already do this using if
inside or outside the loop like on the first examples.”
→ True — But
for-else
offers a more concise, readable, and consistent way to handle these cases, especially when dealing with multiple dynamic failure contexts and more complex scenarios.
“Isn’t using else
confusing since it’s usually tied to if
?”
→ While valid, other languages (Python, etc.) already support
for-else
even with weird or incoherent behaviors. But, in verse, this makes more sense and is super coherent with this proposed behavior due to the presence of failure contexts on the language.
“What if this makes failure behavior less explicit or harder to debug?”
→ On the contrary — this makes failure more explicit and centralized. Rather than scattering fallback checks across multiple conditionals,
for-else
keeps the fallback logic right next to the loop context, making it easier to see what happens when a failure occurs, and easier to test or log accordingly.
All that without needing extra or complex nesting logic both inside or outside the loop such asif
blocks.
Conclusion
Adding for-else
to Verse:
- Enhances clarity when working with failure-based iteration contexts
- Reduces boilerplate in fallback, filtering and nested conditional logic scenarios
- Aligns well with Verse’s failable context system and usage
This proposal does not replace other techniques — it complements them with a more elegant, readable, and easier to use alternative.
For other users and developers reading this feature request, feel free to share any ideas, questions, or additional suggestions related to the topic