Skip to main content
Klar

Collections

Klar provides dynamic collection types: List#[T], Map#[K, V], Set#[T], and Range#[T].

List#[T]

A dynamic, growable array.

Creating Lists

var list: List#[i32] = List.new#[i32]()

Adding Elements

var list: List#[i32] = List.new#[i32]()
list.push(10)
list.push(20)
list.push(30)
// list: [10, 20, 30]

Accessing Elements

let first: i32 = list[0]     // 10
let second: i32 = list[1]    // 20

List Methods

MethodDescription
push(value)Add element to end
pop() -> TRemove and return last element
len() -> i32Number of elements
is_empty() -> boolCheck if empty
clear()Remove all elements
get(index) -> ?TSafe access by index
drop()Free the list's memory

Iterating Lists

var numbers: List#[i32] = List.new#[i32]()
numbers.push(1)
numbers.push(2)
numbers.push(3)

for n: i32 in numbers {
    println("{n}")
}

numbers.drop()  // Clean up

Example: Building a List

fn range_list(start: i32, end: i32) -> List#[i32] {
    var result: List#[i32] = List.new#[i32]()
    for i: i32 in start..end {
        result.push(i)
    }
    return result
}

Map#[K, V]

A hash map storing key-value pairs.

Creating Maps

var scores: Map#[string, i32] = Map.new#[string, i32]()

Adding and Updating

var map: Map#[string, i32] = Map.new#[string, i32]()
map.insert("alice", 100)
map.insert("bob", 85)
map.insert("alice", 95)  // Updates existing key

Accessing Values

let score: i32 = map.get("alice")  // 95

// Check if key exists
if map.contains_key("bob") {
    println("Bob's score: {map.get(\"bob\")}")
}

Map Methods

MethodDescription
insert(key, value)Add or update key-value pair
get(key) -> VGet value for key
contains_key(key) -> boolCheck if key exists
remove(key)Remove key-value pair
len() -> i32Number of entries
is_empty() -> boolCheck if empty
clear()Remove all entries
keys() -> List#[K]Get all keys
values() -> List#[V]Get all values
drop()Free the map's memory

Iterating Maps

Maps iterate as (key, value) tuples:

var scores: Map#[string, i32] = Map.new#[string, i32]()
scores.insert("alice", 100)
scores.insert("bob", 85)

for (name, score) in scores {
    println("{name}: {score}")
}

scores.drop()

Example: Word Counter

fn count_words(words: List#[string]) -> Map#[string, i32] {
    var counts: Map#[string, i32] = Map.new#[string, i32]()

    for word: string in words {
        if counts.contains_key(word) {
            let current: i32 = counts.get(word)
            counts.insert(word, current + 1)
        } else {
            counts.insert(word, 1)
        }
    }

    return counts
}

Set#[T]

A collection of unique values.

Creating Sets

var set: Set#[i32] = Set.new#[i32]()

Adding Elements

var set: Set#[i32] = Set.new#[i32]()
set.insert(10)
set.insert(20)
set.insert(10)  // Duplicate - no effect
// set: {10, 20}

Set Methods

MethodDescription
insert(value)Add element (no-op if exists)
contains(value) -> boolCheck if element exists
remove(value)Remove element
len() -> i32Number of elements
is_empty() -> boolCheck if empty
clear()Remove all elements
drop()Free the set's memory

Iterating Sets

var numbers: Set#[i32] = Set.new#[i32]()
numbers.insert(3)
numbers.insert(1)
numbers.insert(4)

for n: i32 in numbers {
    println("{n}")
}
// Note: Order is not guaranteed

numbers.drop()

Example: Finding Unique Values

fn unique(list: List#[i32]) -> List#[i32] {
    var seen: Set#[i32] = Set.new#[i32]()
    var result: List#[i32] = List.new#[i32]()

    for n: i32 in list {
        if not seen.contains(n) {
            seen.insert(n)
            result.push(n)
        }
    }

    seen.drop()
    return result
}

Range#[T]

A range of values, typically used for iteration.

Creating Ranges

// Exclusive range: 0, 1, 2, 3, 4
let exclusive: Range#[i32] = 0..5

// Inclusive range: 0, 1, 2, 3, 4, 5
let inclusive: Range#[i32] = 0..=5

Range Properties

let r: Range#[i32] = 0..10

let start: i32 = r.start    // 0
let end: i32 = r.end        // 10
let is_inc: bool = r.inclusive  // false

Iterating Ranges

// Most common: directly in for loop
for i: i32 in 0..5 {
    println("{i}")  // 0, 1, 2, 3, 4
}

// As a variable
var r: Range#[i32] = 1..=3
for x: i32 in r {
    println("{x}")  // 1, 2, 3
}

Range with Different Types

// Ranges work with any numeric type
for i: i64 in 0.as#[i64]..100.as#[i64] {
    // ...
}

Memory Management

Collections allocate memory on the heap. Call drop() when done:

fn process() -> void {
    var list: List#[i32] = List.new#[i32]()
    list.push(1)
    list.push(2)

    // Use the list...

    list.drop()  // Free memory
}

What drop() Frees

The drop() method frees only the collection's backing array, not any heap allocations owned by elements within the collection.

Element Typedrop() Behavior
Primitives (i32, bool, etc.)Fully cleaned up
Structs (value types)Fully cleaned up
StringLeaks - String's buffer not freed
List#[T]Leaks - Inner list's array not freed
Rc#[T], Arc#[T]Reference count decremented properly

Example of leak:

var names: List#[String] = List.new#[String]()
names.push("alice".to_string())
names.push("bob".to_string())
names.drop()  // Frees the List's array, but "alice" and "bob" buffers leak!

Nested Collections

For nested collections, you must manually drop inner elements:

var matrix: List#[List#[i32]] = List.new#[List#[i32]]()

var row1: List#[i32] = List.new#[i32]()
row1.push(1)
row1.push(2)
matrix.push(row1)

var row2: List#[i32] = List.new#[i32]()
row2.push(3)
row2.push(4)
matrix.push(row2)

// Clean up: iterate and drop each row, then drop matrix
for row: List#[i32] in matrix {
    row.drop()
}
matrix.drop()

Note: Future versions of Klar may implement automatic nested cleanup via the Drop trait for collection types.

Example: Graph Adjacency List

struct Graph {
    edges: Map#[i32, List#[i32]],
}

impl Graph {
    fn new() -> Graph {
        return Graph { edges: Map.new#[i32, List#[i32]]() }
    }

    fn add_edge(inout self: Graph, from: i32, to: i32) -> void {
        if not self.edges.contains_key(from) {
            self.edges.insert(from, List.new#[i32]())
        }
        var neighbors: List#[i32] = self.edges.get(from)
        neighbors.push(to)
    }

    fn drop(inout self: Graph) -> void {
        // Drop all neighbor lists, then the map
        for (_, neighbors) in self.edges {
            neighbors.drop()
        }
        self.edges.drop()
    }
}

Next Steps