hanki

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