Why AILANG Has No Loops: Formal Rationale
Status: Design Decision (Permanent) Version: v0.3.14+ Related: README ยง No Loops
Executive Summaryโ
AILANG intentionally excludes for
, while
, loop
, and all open-ended iteration constructs. This design decision prioritizes:
- Deterministic semantics - iteration defined by data structure, not control flow
- Total functions - all recursion must be provably terminating
- Effect transparency - iteration cannot hide side effects
- Compositional reasoning - algebraic laws enable safe transformation
- AI-friendly structure - explicit data dependencies instead of implicit state
This document provides the formal rationale, algebraic foundations, and implementation guidance.
Problem: Loops Are Semantically Opaqueโ
Traditional Loop Modelโ
# Python: mutable state + implicit control flow
sum = 0
for i in range(0, 10):
sum = sum + i
Hidden assumptions:
- Loop counter
i
lives in mutable scope - Termination depends on
range(0, 10)
evaluation sum
is mutated in-place (requires pointer semantics)- Iteration order is sequential (cannot parallelize)
- Side effects can occur arbitrarily (I/O, exceptions, network)
For AI code synthesis:
- โ Cannot infer iteration count without runtime evaluation
- โ Cannot prove termination (halting problem)
- โ Cannot determine data dependencies
- โ Cannot reason about effect isolation
- โ Cannot optimize (fusion, parallelization) without conservative analysis
Solution: Total Recursion + Algebraic Combinatorsโ
AILANG replaces loops with total, structurally recursive functions and higher-order combinators that obey algebraic laws.
Approach 1: Pattern Matching (Structural Recursion)โ
func sum(list: List[Int]) -> Int {
match list {
[] => 0,
[x, ...xs] => x + sum(xs)
}
}
Guarantees:
- โ
Termination: recursion depth =
length(list)
(finite) - โ
Effect-free: no
! {IO}
in signature โ pure computation - โ
Compositional:
sum([1,2,3])
reduces deterministically - โ AI-readable: structure mirrors data (cons list โ cons recursion)
Approach 2: Fold Combinators (Catamorphisms)โ
foldl(range(0, 10), 0, \acc, i. acc + i)
Guarantees:
- โ
Termination:
foldl
traverses finite structure once - โ Effect-safe: effects constrained to accumulator function
- โ Algebraic: obeys fold laws (associativity, identity)
- โ Optimizable: compiler can fuse adjacent folds
Algebraic Laws (Equational Reasoning)โ
AILANG's iteration primitives obey equational laws that enable safe transformation.
Fold Lawsโ
Identity:
foldl([], z, f) = z
foldr([], z, f) = z
Fusion (fold-after-map):
foldl(map(xs, g), z, f) = foldl(xs, z, \acc, x. f(acc, g(x)))
Associativity (for commutative operators):
foldl(xs ++ ys, z, f) = foldl(ys, foldl(xs, z, f), f)
Map Lawsโ
Identity:
map(xs, \x. x) = xs
Composition:
map(map(xs, f), g) = map(xs, \x. g(f(x)))
Fusion (map-after-map):
map(xs, g) . map(xs, f) = map(xs, \x. g(f(x)))
Effect Preservationโ
Pure functions preserve purity:
map : (a -> b) -> List[a] -> List[b]
Effectful functions propagate effects:
mapM : (a -> b ! {E}) -> List[a] -> List[b] ! {E}
Explicit effect threading:
foldlM : (acc -> a -> acc ! {E}) -> acc -> List[a] -> acc ! {E}
These laws do not hold for imperative loops (mutation breaks equational reasoning).
Comparison: Loops vs. Total Recursionโ
Property | Imperative Loops | AILANG Recursion |
---|---|---|
Termination | Undecidable (halting problem) | Guaranteed (structural induction) |
Effect tracking | Hidden (implicit state) | Explicit (! {IO} , ! {FS} ) |
Parallelization | Requires analysis | Free (map/fold are parallelizable) |
Equational reasoning | Breaks (mutation) | Holds (pure functions) |
AI synthesis | Context-dependent | Context-free |
Verification | Requires annotations | Type-driven |
Implementation Patternsโ
Pattern 1: Finite Iteration (Fixed Count)โ
Problem: Loop N times
Imperative:
for i in range(0, N):
print(i)
AILANG:
import std/list (range, forEach)
forEach(range(0, N), \i. println(show(i)))
Type: forEach : List[a] -> (a -> () ! {E}) -> () ! {E}
Pattern 2: Aggregation (Reduce)โ
Problem: Sum a list
Imperative:
total = 0
for x in xs:
total += x
AILANG:
import std/list (foldl)
foldl(xs, 0, \acc, x. acc + x)
Type: foldl : (acc -> a -> acc) -> acc -> List[a] -> acc
Pattern 3: Early Termination (Find)โ
Problem: Find first element matching predicate
Imperative:
for x in xs:
if predicate(x):
return x
AILANG:
import std/list (find)
find(xs, predicate) -- Returns Option[a]
Type: find : List[a] -> (a -> Bool) -> Option[a]
Pattern 4: Stateful Iteration (Scan)โ
Problem: Compute running totals
Imperative:
acc = 0
results = []
for x in xs:
acc += x
results.append(acc)
AILANG:
import std/list (scanl)
scanl(xs, 0, \acc, x. acc + x) -- Returns List[acc]
Type: scanl : (acc -> a -> acc) -> acc -> List[a] -> List[acc]
Pattern 5: Conditional Filtering (Filter + Map)โ
Problem: Transform elements meeting criteria
Imperative:
results = []
for x in xs:
if x > 0:
results.append(x * 2)
AILANG:
import std/list (map, filter)
map(filter(xs, \x. x > 0), \x. x * 2)
Or with future comprehension syntax:
[ x * 2 for x in xs if x > 0 ]
Total Recursion Guaranteesโ
AILANG enforces structural recursion to guarantee termination.
Valid: Decreasing Argument Sizeโ
func factorial(n: Int) -> Int {
if n <= 0 then 1
else n * factorial(n - 1) -- โ
n-1 < n
}
Invalid: Non-decreasing Recursionโ
func loop(n: Int) -> Int {
loop(n + 1) -- โ REJECTED: n+1 >= n (non-terminating)
}
Structural Induction on ADTsโ
type Tree = Leaf(Int) | Node(Tree, Int, Tree)
func sumTree(t: Tree) -> Int {
match t {
Leaf(x) => x,
Node(l, x, r) => sumTree(l) + x + sumTree(r) -- โ
l,r are subterms of t
}
}
Termination proof: Recursion descends into strict subterms of the ADT.
Effect Transparencyโ
Loops can hide arbitrary effects. AILANG makes effects explicit and trackable.
Example: Pure Iterationโ
map(xs, \x. x * 2) -- Type: List[Int] -> List[Int]
No ! {E}
in signature โ guaranteed pure (no I/O, no network, no mutation).
Example: Effectful Iterationโ
import std/io (println)
import std/list (forEach)
forEach(xs, \x. println(show(x)))
-- Type: List[Int] -> () ! {IO}
Effect signature ! {IO}
makes side effects visible in the type.
Example: Effect Propagationโ
import std/fs (readFile)
import std/list (mapM)
func loadFiles(paths: List[String]) -> List[String] ! {FS} {
mapM(paths, \path. readFile(path))
}
mapM
propagates ! {FS}
from inner function to outer result.
With loops: Effects are hidden in imperative control flow. With AILANG: Effects are type-checked and validated.
Optimization Opportunitiesโ
Fusion (Deforestation)โ
Naive (two passes):
map(map(xs, \x. x + 1), \x. x * 2)
Fused (one pass):
map(xs, \x. (x + 1) * 2)
Compiler applies map fusion law automatically.
Parallelizationโ
Sequential fold:
foldl(xs, 0, \acc, x. acc + x)
Parallel reduction (if operator is associative):
reduce(xs, 0, \a, b. a + b) -- Can split across threads
AILANG can detect associative operators and parallelize safely.
Short-circuit Evaluationโ
Find first match:
find(xs, \x. expensivePredicate(x))
Compiler knows find
stops on first match (not possible with for
loops).
Future: List Comprehensionsโ
AILANG may add comprehension syntax as syntactic sugar (no new semantics).
Proposed Syntaxโ
[ p.name for p in people if p.age >= 30 ]
Desugars Toโ
map(filter(people, \p. p.age >= 30), \p. p.name)
Key constraint: Comprehensions must desugar to existing combinators (no new runtime behavior).
Benefitsโ
- Readable syntax for data transformations
- Deterministic desugaring (no hidden state)
- Preserves effect tracking (if
p.age
has effects, comprehension propagates them) - AI-understandable (maps directly to algebraic laws)
Conclusionโ
Loops are a human-centric abstraction that obscures structure. AILANG replaces them with:
- Total recursion (pattern matching, structural induction)
- Algebraic combinators (map, fold, filter, scan)
- Effect-typed iteration (explicit effect propagation)
- Equational reasoning (fusion, parallelization, verification)
This design makes AILANG deterministic, verifiable, and AI-friendly โ at the cost of syntactic familiarity.
For developers: Think in data transformations, not control flow. For AIs: Reason about functions, not state machines.
Referencesโ
- Fold Laws: Bird & Wadler, Introduction to Functional Programming
- Totality Checking: Idris Documentation on Totality
- Effect Systems: Koka Effect Handlers
- Fusion Optimization: Stream Fusion: From Lists to Streams to Nothing at All (PDF)
See Alsoโ
- README ยง No Loops - User-facing explanation
- Implementation Status - Current language features and limitations
- Getting Started - Installation and quick start