List
Calcit List is a persistent, immutable vector. In Rust it uses ternary-tree (optimized 2-3 tree with finger-tree tricks). In JavaScript it uses a similar structure with a fast-path CalcitSliceList for append-heavy workloads.
All list operations return new lists — the original is never mutated.
Quick Recipes
- Create:
[] 1 2 3orrange 5 - Access:
nth xs 0,first xs,last xs - Modify:
append xs 4,prepend xs 0,assoc xs 1 99 - Transform:
map xs f,filter xs f,reduce xs 0 f - Combine:
concat xs ys,slice xs 1 3
Creating Lists
let
empty-list $ []
nums $ [] 1 2 3 4 5
words $ [] |foo |bar |baz
println nums
; => ([] 1 2 3 4 5)
range generates a sequence:
let
r1 $ range 5
r2 $ range 2 7
println r1
; => ([] 0 1 2 3 4)
println r2
; => ([] 2 3 4 5 6)
Accessing Elements
let
xs $ [] 10 20 30 40
println $ nth xs 0
; => 10
println $ first xs
; => 10
println $ last xs
; => 40
println $ count xs
; => 4
get is an alias for nth:
let
xs $ [] :a :b :c
println $ get xs 1
; => :b
Adding / Removing Elements
let
xs $ [] 1 2 3
println $ append xs 4
; => ([] 1 2 3 4)
println $ prepend xs 0
; => ([] 0 1 2 3)
println $ conj xs 4 5
; => ([] 1 2 3 4 5)
println $ concat xs $ [] 4 5
; => ([] 1 2 3 4 5)
Update or remove by index:
let
xs $ [] 1 2 3
println $ assoc xs 1 99
; => ([] 1 99 3)
println $ dissoc xs 1
; => ([] 1 3)
Slicing & Reordering
let
xs $ [] 1 2 3 4 5
println $ rest xs
; => ([] 2 3 4 5)
println $ butlast xs
; => ([] 1 2 3 4)
println $ slice xs 1 3
; => ([] 2 3)
println $ take xs 3
; => ([] 1 2 3)
println $ take-last xs 2
; => ([] 4 5)
println $ drop xs 2
; => ([] 3 4 5)
Sort (default ascending):
let
xs $ [] 3 1 4 1 5
println $ sort xs
; => ([] 1 1 3 4 5)
Sort by key function (method-style):
let
xs $ [] 1 2 3 4 5
println $ .sort-by xs $ fn (x) (- 0 x)
; => ([] 5 4 3 2 1)
Reverse:
let
xs $ [] 1 2 3 4 5
println $ reverse xs
; => ([] 5 4 3 2 1)
Filtering & Finding
let
xs $ [] 1 2 3 4 5
println $ filter xs $ fn (x) (> x 3)
; => ([] 4 5)
println $ filter-not xs $ fn (x) (> x 3)
; => ([] 1 2 3)
println $ find xs $ fn (x) (> x 3)
; => 4
println $ find-index xs $ fn (x) (> x 3)
; => 3
println $ index-of xs 3
; => 2
Transforming
let
xs $ [] 1 2 3 4 5
println $ map xs $ fn (x) (* x 2)
; => ([] 2 4 6 8 10)
println $ map-indexed xs $ fn (i x) ([] i x)
; => ([] ([] 0 1) ([] 1 2) ([] 2 3) ([] 3 4) ([] 4 5))
Flatten one level of nesting (method-style):
let
nested $ [] ([] 1 2) ([] 3 4) ([] 5)
println $ .flatten nested
; => ([] 1 2 3 4 5)
Aggregating
let
xs $ [] 1 2 3 4 5
println $ reduce xs 0 $ fn (acc x) (+ acc x)
; => 15
println $ foldl xs 0 $ fn (acc x) (+ acc x)
; => 15
println $ any? xs $ fn (x) (> x 4)
; => true
println $ every? xs $ fn (x) (> x 0)
; => true
group-by partitions into a map keyed by the return value of the function:
let
xs $ [] 1 2 3 4 5
println $ group-by xs $ fn (x) (if (> x 3) :big :small)
; => ({} (:big ([] 4 5)) (:small ([] 1 2 3)))
Strings from Lists
let
words $ [] |hello |world |foo
println $ join-str words |,
; => hello,world,foo
Converting
let
xs $ [] 1 2 2 3 3 3
println $ .to-set xs
; => (#{} 1 2 3)
Thread Macro Pipelines
The -> thread macro is idiomatic for list transformations:
let
result $ -> (range 10)
filter $ fn (x) (> x 5)
map $ fn (x) (* x x)
println result
; => ([] 36 49 64 81)
Common Patterns
Building lists incrementally
let
source $ [] 1 2 3 4 5
init $ []
result $ foldl source init $ fn (acc item)
if (> item 2)
append acc (* item 10)
, acc
println result
; => ([] 30 40 50)
Zip two lists together
let
ks $ [] :a :b :c
vs $ [] 1 2 3
zipped $ map-indexed ks $ fn (i k) ([] k (nth vs i))
println zipped
; => ([] ([] :a 1) ([] :b 2) ([] :c 3))
Deduplicate
Convert to set (removes duplicates, loses order):
let
xs $ [] 1 2 2 3 3 3
println $ .to-set xs
; => (#{} 1 2 3)
Implementation Notes
nthandgetare O(log n) on the ternary tree structure.appendandprependare amortized O(1) in the Rust implementation.concatis O(m) where m is the size of the appended list.- Lists are zero-indexed.