range
stdlib/core/range.hk: Range, half-open [lo, hi) integer iteration.
It fills the no-while, no-for, no-ranges gap with stdlib and no syntax: a counting loop is Range.new(0, n).each!(|i| ...) in place of a hand-rolled var/loop/if/break. The combinator surface (each!/map/fold) mirrors List, so a range drops into the same pipelines. int.upto (inclusive) and int.times! (see int.hk) are the ergonomic entry points built on top.
Bounds are int, the default numeric tier, and never a fixed-width type.
Range
opaque Range
lo: int
hi: int
end
A half-open integer range [lo, hi): iteration covers lo, lo+1, … hi-1. Empty when hi <= lo. Construct with Range.new or the inclusive int.upto.
impl Range
new
def new(lo: int, hi: int) -> Range
The half-open range [lo, hi).
Range.new(0, 3).length => 3
Range.new(3, 3).length => 0
length
prop length(self) -> int
The element count: hi - lo, clamped at 0 for an empty range.
Range.new(2, 7).length => 5
Range.new(7, 2).length => 0
each!
def each!(self, f: (int) -> () [e]) -> () [e]
Applies the effectful f to each element in order, for its effects. A counting loop and no recursion, and a large range adds no call-stack depth. @no-doctest: effectful iteration returning unit, with no value to assert
map
def map(self, f: (int) -> U) -> List<U>
The list of f applied to each element, in order. Mirrors List.map. A counting loop like each!, and a large range adds no call-stack depth.
Range.new(0, 4).map(|i| i * i).length => 4
fold
def fold(self, init: U, f: (U, int) -> U) -> U
The left fold of f over the elements: f(…f(f(init, lo), lo+1)…). An empty range folds to init. Mirrors List.fold. A counting loop like each!, and a large range adds no call-stack depth.
Range.new(0, 5).fold(0, |acc, i| acc + i) => 10
Range.new(0, 0).fold(99, |acc, i| acc + i) => 99
to_list
def to_list(self) -> List<int>
The elements as a List<int>.
Range.new(2, 5).to_list().length => 3