Arrays
Arrays are ordered collections of elements that provide efficient sequential storage and access. Harneet supports both untyped arrays and typed arrays with comprehensive instance methods.
Overview
Arrays in Harneet are: - Ordered - elements maintain insertion order - Zero-indexed - first element is at index 0 - Dynamic - can grow and shrink with instance methods - Type-safe - typed arrays enforce element type constraints - Method-rich - built-in .length(), .indexOf(), .contains(), .concat(), .reverse(), .push(), .pop(), .shift(), .unshift(), .clear() methods - Sliceable - use arr[start:end] operator for slicing - Flexible - untyped arrays can hold mixed types
Array Literals
You can create an array using an array literal:
| Array Literals |
|---|
| package main
var numbers = [1, 2, 3, 4, 5]
var names = ["Alice", "Bob", "Charlie"]
var empty = []
|
Typed Arrays
Typed arrays enforce element type constraints at compile time:
| Typed Arrays |
|---|
| package main
var numbers int[5] = [1, 2, 3, 4, 5]
var names string[3] = ["Alice", "Bob", "Charlie"]
// Type-safe operations
numbers[0] = 10 // ✅ Valid
// numbers[0] = "text" // ❌ Type error
|
Mixed Types
Untyped arrays can hold elements of different types:
| Mixed Types |
|---|
| package main
var mixed = [1, "hello", 3.14, true]
|
Nested Arrays
You can create nested arrays (arrays of arrays):
| Nested Arrays |
|---|
| package main
var matrix = [[1, 2], [3, 4]]
var cube = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
Array Instance Methods
Arrays support several built-in methods for common operations. All methods enforce None-receiver safety (calling a method on None raises a runtime error).
Read-Only Methods
length()
Returns the number of elements in the array:
| length() |
|---|
| package main
var arr = [1, 2, 3, 4, 5]
var len = arr.length() // 5
var empty = []
var emptyLen = empty.length() // 0
|
indexOf(x)
Returns the index of the first occurrence of element x, or -1 if not found:
| indexOf() |
|---|
| package main
var fruits = ["apple", "banana", "orange", "banana"]
var idx = fruits.indexOf("banana") // 1
var missing = fruits.indexOf("grape") // -1
// Uses deep equality for comparison
var nested = [[1, 2], [3, 4], [5, 6]]
var nestedIdx = nested.indexOf([3, 4]) // 1
|
contains(x)
Returns true if the array contains element x, false otherwise:
| contains() |
|---|
| package main
var numbers = [1, 2, 3, 4, 5]
var hasThree = numbers.contains(3) // true
var hasTen = numbers.contains(10) // false
// Works with any type
var mixed = [1, "hello", true]
var hasString = mixed.contains("hello") // true
|
Pure-Return Methods
These methods return new arrays without modifying the original:
concat(other)
Returns a new array containing all elements from this array followed by all elements from other:
| concat() |
|---|
| package main
import fmt
var a = [1, 2, 3]
var b = [4, 5, 6]
var combined = a.concat(b) // [1, 2, 3, 4, 5, 6]
// Original arrays unchanged
fmt.Println(a) // [1, 2, 3]
fmt.Println(b) // [4, 5, 6]
|
reverse()
Returns a new array with elements in reverse order:
| reverse() |
|---|
| package main
import fmt
var arr = [1, 2, 3, 4, 5]
var reversed = arr.reverse() // [5, 4, 3, 2, 1]
// Original array unchanged
fmt.Println(arr) // [1, 2, 3, 4, 5]
|
Mutator Methods
These methods modify the array in place:
push(x)
Adds element x to the end of the array and returns the array:
| push() |
|---|
| package main
import fmt
var arr = [1, 2, 3]
arr.push(4)
fmt.Println(arr) // [1, 2, 3, 4]
// Chaining
arr.push(5).push(6)
fmt.Println(arr) // [1, 2, 3, 4, 5, 6]
|
pop()
Removes and returns the last element as a tuple (value, error). Returns (None, error) if array is empty:
| pop() |
|---|
| package main
import fmt
var arr = [1, 2, 3]
var val, err = arr.pop()
// val = 3, err = None
fmt.Println(arr) // [1, 2]
// Empty array
var empty = []
var emptyVal, emptyErr = empty.pop()
// emptyVal = None, emptyErr = error("pop from empty array")
|
unshift(x)
Adds element x to the beginning of the array and returns the array:
| unshift() |
|---|
| package main
import fmt
var arr = [2, 3, 4]
arr.unshift(1)
fmt.Println(arr) // [1, 2, 3, 4]
|
shift()
Removes and returns the first element as a tuple (value, error). Returns (None, error) if array is empty:
| shift() |
|---|
| package main
import fmt
var arr = [1, 2, 3]
var val, err = arr.shift()
// val = 1, err = None
fmt.Println(arr) // [2, 3]
// Empty array
var empty = []
var emptyVal, emptyErr = empty.shift()
// emptyVal = None, emptyErr = error("shift from empty array")
|
clear()
Removes all elements from the array:
| clear() |
|---|
| package main
import fmt
var arr = [1, 2, 3, 4, 5]
arr.clear()
fmt.Println(arr) // []
fmt.Println(arr.length()) // 0
|
Typed Arrays and Methods
Typed arrays preserve their element type through all operations:
| Typed Array Methods |
|---|
| package main
var numbers int[3] = [1, 2, 3]
// Slice operator returns typed array
var sliced = numbers[0:2] // int[] with [1, 2]
// concat() enforces type compatibility
var more int[] = [4, 5, 6]
var combined = numbers.concat(more) // int[] with [1, 2, 3, 4, 5, 6]
// reverse() returns typed array
var reversed = numbers.reverse() // int[] with [3, 2, 1]
// push() enforces element type
numbers.push(4) // ✅ Valid
// numbers.push("text") // ❌ Type error
|
None-Receiver Safety
All array instance methods enforce None-receiver safety. Calling a method on None raises a runtime error:
| None-Receiver Safety |
|---|
| package main
var arr array[int] = None
// All of these will raise runtime errors:
// arr.length() // ERROR: type null does not have method 'length'
// arr.push(1) // ERROR: type null does not have method 'push'
// arr.contains(1) // ERROR: type null does not have method 'contains'
|
Slicing
Harneet supports Go-like slicing on arrays, typed arrays, and strings using the s[a:b] syntax (also [:b], [a:], and [:]). Indices are integers, clamped to valid bounds; the slice is half-open (a inclusive, b exclusive); if a > b, the result is empty.
See the Slicing section for full semantics and examples: arrays module documentation.
Complete Example
| Complete Example |
|---|
| package main
import fmt
import assert
var fruits = ["apple", "banana", "orange"]
// Read-only methods
fmt.Println("Length:", fruits.length()) // 3
fmt.Println("Index of banana:", fruits.indexOf("banana")) // 1
fmt.Println("Contains orange:", fruits.contains("orange")) // true
// Slice operator (creates new array, doesn't modify original)
var sliced = fruits[0:2] // ["apple", "banana"]
// Pure-return methods (don't modify original)
var reversed = fruits.reverse()
var combined = fruits.concat(["grape", "melon"])
fmt.Println("Sliced:", sliced) // ["apple", "banana"]
fmt.Println("Reversed:", reversed) // ["orange", "banana", "apple"]
fmt.Println("Combined:", combined) // ["apple", "banana", "orange", "grape", "melon"]
fmt.Println("Original:", fruits) // ["apple", "banana", "orange"] (unchanged)
// Mutator methods (modify in place)
fruits.push("grape")
fmt.Println("After push:", fruits) // ["apple", "banana", "orange", "grape"]
var last, err = fruits.pop()
fmt.Println("Popped:", last, "Error:", err) // "grape", None
fmt.Println("After pop:", fruits) // ["apple", "banana", "orange"]
fruits.unshift("strawberry")
fmt.Println("After unshift:", fruits) // ["strawberry", "apple", "banana", "orange"]
var first, err2 = fruits.shift()
fmt.Println("Shifted:", first, "Error:", err2) // "strawberry", None
fmt.Println("After shift:", fruits) // ["apple", "banana", "orange"]
fruits.clear()
fmt.Println("After clear:", fruits) // []
|
Array Operations
For additional array manipulation functions, see the arrays module documentation.