Skip to content

Slices

Slices are a fundamental operator in Harneet that allow you to extract portions of arrays, strings, and typed arrays using the [start:end] syntax. This Go-like slicing syntax is consistent across all sequence types.

Overview

Slicing in Harneet: - Syntax: sequence[start:end], sequence[:end], sequence[start:], sequence[:] - Universal: Works on arrays, typed arrays, and strings - Half-open interval: start is inclusive, end is exclusive - Safe: Automatically clamps indices to valid bounds - Type-preserving: Typed arrays preserve their element type

Slice Syntax

Harneet supports four forms of slice notation:

Syntax Description Example
seq[a:b] From index a to b (exclusive) arr[1:4] → elements at indices 1, 2, 3
seq[:b] From start to b (exclusive) arr[:3] → first 3 elements
seq[a:] From index a to end arr[2:] → from index 2 onwards
seq[:] Full copy arr[:] → complete shallow copy

Array Slicing

Slicing arrays creates new arrays with the specified elements:

Array Slicing
package main
import fmt

var arr = [10, 20, 30, 40, 50]

// Basic slicing
var mid = arr[1:4]        // [20, 30, 40]
var first = arr[:3]       // [10, 20, 30]
var last = arr[2:]        // [30, 40, 50]
var copy = arr[:]         // [10, 20, 30, 40, 50]

// Empty slices
var empty1 = arr[2:2]     // []
var empty2 = arr[5:5]     // []

// Out of bounds - automatically clamped
var clamped1 = arr[-5:2]  // Treated as arr[0:2] → [10, 20]
var clamped2 = arr[3:99]  // Treated as arr[3:5] → [40, 50]

// Start > end = empty
var empty3 = arr[4:2]     // []

fmt.Println("Mid:", mid)
fmt.Println("First:", first)
fmt.Println("Last:", last)

String Slicing

String slicing extracts substrings (currently byte-based):

String Slicing
package main
import fmt

var text = "Hello, World!"

// Basic slicing
var hello = text[0:5]     // "Hello"
var world = text[7:12]    // "World"
var greeting = text[:5]   // "Hello"
var rest = text[7:]       // "World!"
var copy = text[:]        // "Hello, World!"

// Empty slices
var empty = text[5:5]     // ""

fmt.Println("Hello:", hello)
fmt.Println("World:", world)

Note: String slicing is currently byte-based. For multi-byte UTF-8 characters, ensure indices align with character boundaries to avoid splitting characters.

UTF-8 Considerations
package main
import fmt

var text = "Hello 世界"

// Be careful with multi-byte characters
// "世" is 3 bytes in UTF-8
var substr = text[6:9]   // First Chinese character (bytes 6-8)
fmt.Println(substr)      // "世"

// Safer: use string functions for complex Unicode handling

Typed Array Slicing

Typed arrays preserve their element type when sliced:

Typed Array Slicing
package main
import fmt

var numbers int[5] = [1, 2, 3, 4, 5]

// Slicing preserves type
var slice1 = numbers[1:4]  // int[] with [2, 3, 4]
var slice2 = numbers[:2]   // int[] with [1, 2]

// Type safety maintained
fmt.Println(slice1)  // [2, 3, 4]

// Can only assign compatible typed arrays
var moreNumbers int[] = [6, 7, 8]
var combined = slice1.concat(moreNumbers)  // Works - same type

Slicing Rules

1. Index Bounds

Indices are automatically clamped to valid ranges:

Index Clamping
package main
var arr = [1, 2, 3, 4, 5]

// Negative indices clamped to 0
var slice1 = arr[-10:3]  // Same as arr[0:3] → [1, 2, 3]

// Indices beyond length clamped to len
var slice2 = arr[2:100]  // Same as arr[2:5] → [3, 4, 5]

// Both clamped
var slice3 = arr[-5:10]  // Same as arr[0:5] → [1, 2, 3, 4, 5]

2. Empty Slices

When start >= end, the result is an empty slice:

Empty Slices
1
2
3
4
5
6
7
package main
var arr = [1, 2, 3, 4, 5]

var empty1 = arr[3:3]    // []
var empty2 = arr[5:5]    // []
var empty3 = arr[4:2]    // start > end → []
var empty4 = arr[10:15]  // Both beyond bounds → []

3. Half-Open Interval

The slice is half-open: start is inclusive, end is exclusive:

Half-Open Interval
1
2
3
4
package main
var arr = [0, 1, 2, 3, 4]

var slice = arr[1:3]  // Indices 1 and 2 (not 3) → [1, 2]

4. Shallow Copy

Slicing creates a new collection with copied elements (shallow copy):

Shallow Copy
1
2
3
4
5
6
7
8
package main
var original = [1, 2, 3, 4, 5]
var copy = original[:]

// Modifying copy doesn't affect original
copy.push(6)
fmt.Println("Original:", original)  // [1, 2, 3, 4, 5]
fmt.Println("Copy:", copy)          // [1, 2, 3, 4, 5, 6]

Use Cases

Extracting Subsets

Extract Elements
package main
var scores = [95, 87, 92, 88, 91]

// Top 3 scores (assuming sorted descending)
var top3 = scores[:3]  // [95, 87, 92]

// Last 2 scores
var last2 = scores[3:]  // [88, 91]

// Middle scores
var middle = scores[1:4]  // [87, 92, 88]

Pagination

Pagination
1
2
3
4
5
6
7
8
package main
var items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var pageSize = 3
var pageNum = 1  // 0-indexed

var start = pageNum * pageSize
var end = start + pageSize
var page = items[start:end]  // [4, 5, 6]

String Manipulation

String Operations
package main
var filename = "document.txt"

// Extract extension
var dotIndex = filename.lastIndexOf(".")
var extension = filename[dotIndex:]  // ".txt"

// Extract name without extension
var name = filename[:dotIndex]  // "document"

// First N characters
var prefix = filename[:3]  // "doc"

Removing Elements

Remove by Slicing
1
2
3
4
5
6
7
package main
var arr = [1, 2, 3, 4, 5]

// Remove element at index 2 (value 3)
var before = arr[:2]     // [1, 2]
var after = arr[3:]      // [4, 5]
var result = before.concat(after)  // [1, 2, 4, 5]

Complete Example

Complete Slicing Example
package main
import fmt

// Array slicing
var numbers = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

fmt.Println("Original:", numbers)
fmt.Println("First half:", numbers[:5])
fmt.Println("Second half:", numbers[5:])
fmt.Println("Middle:", numbers[3:7])
fmt.Println("Every other (manual):", [numbers[0], numbers[2], numbers[4]])

// String slicing
var message = "Hello, Harneet Programming Language!"

fmt.Println("\nString:", message)
fmt.Println("Greeting:", message[:5])
fmt.Println("Language:", message[23:])
fmt.Println("Without punctuation:", message[:len(message)-1])

// Typed array slicing
var scores int[] = [95, 87, 92, 88, 91, 85, 94]

var topScores = scores[:3]
var avgScores = scores[3:5]
var lowScores = scores[5:]

fmt.Println("\nTop scores:", topScores)
fmt.Println("Average scores:", avgScores)
fmt.Println("Low scores:", lowScores)

// Copy and modify
var scoresCopy = scores[:]
scoresCopy.push(100)

fmt.Println("\nOriginal unchanged:", scores)
fmt.Println("Copy with new score:", scoresCopy)

Comparison with Other Languages

Language Syntax Notes
Harneet seq[a:b], seq[:b], seq[a:], seq[:] Go-like, auto-clamps bounds
Go seq[a:b], seq[:b], seq[a:], seq[:] Panics on out-of-bounds
Python seq[a:b], seq[:b], seq[a:], seq[:] Supports negative indices
JavaScript seq.slice(a, b) Method, not operator
Rust &seq[a..b], &seq[..b], &seq[a..] Creates references

Best Practices

1. Use Slice Operator for Clarity

Clear Intent
1
2
3
4
5
6
7
8
// Preferred: Clear slice syntax
var first10 = items[:10]

// Less clear: Manual loop
var first10 = []
for i = 0; i < 10; i = i + 1 {
    first10.push(items[i])
}

2. Prefer Slicing Over Index Loops

Prefer Slicing
1
2
3
4
5
6
7
8
// Good: Simple and clear
var subset = arr[5:10]

// Avoid: Unnecessary complexity
var subset = []
for i = 5; i < 10; i = i + 1 {
    subset.push(arr[i])
}

3. Be Mindful of UTF-8 with Strings

UTF-8 Safety
package main
import strings

// For ASCII strings: slice freely
var ascii = "Hello"
var part = ascii[0:3]  // "Hel"

// For Unicode: be careful or use string functions
var unicode = "你好世界"
// Prefer: use string methods for complex operations
var chars = strings.Split(unicode, "")

See Also