Ki has two fundamental sequential data types:

  • Array: array
  • Dynamic array: dynarray

Arrays And Dynamic Arrays

Arrays and dynamic arrays are very similar. Both have:

  • An element type
  • An element count

The difference is that an array's element count cannot be changed, whereas a dynarray's element not only can be changed, it will automatically resize as elements are added to or removed from it.

fn main() {
    # Initialize an array with 3 elements.
    var fruits: array(["apple", "banana", "cherry"])

    # Initialize a dynarray with 1 element; then add another.
    var vegetables: dynarray(["asparagus"])


    # Initialize an array with 3 empty elements; then fill them.
    var planets: array(@type: string, count: 3)

    echo("Each element of planets is the empty string ({{planets[0]}}), see?")

    planets[0] = "Mercury"
    planets[1] = "Venus"
    planets[2] = "Earth"

    # planets contains pointers.  If you want to reserve the memory, use
    # constructors.
    var planets2: array(string(length: 7), count: 3)

    planets2[0] = "Mars"
    planets2[1] = "Jupiter"
    planets2[2] = "Saturn"

    # Initialize a dynarray; reserve some space; then add some elements using
    # indexing.
    var trees: dynarray(type: <str>)

    with (t!: trees.ensure_capacity(4)) {
        t[0] = "oak"
        t[1] = "maple"
        t[2] = "pine"
        t[3] = "maple"

Strings And Dynamic Strings

Strings and dynamic strings are fundamentally an array (or dynarray, respectively) with the element type set to rune. In addition to this, they also provide a useful interface for string management. For more information, see strings

Linked Lists

Ki provides a linked list simply called list.

type Player {
    var name: *"Player"
    var health: 0

fn main() {
    val players1: list(
        type: *<Player>!

    list.append(*Player("Player 1", 10)!)

    val players2: list([
        *Player(*"Player 1", 10)!,
        *Player(*"Player 2", 10)!


You have two options when indexing into a container. First, you can use with:

fn main() {
    var names: dynarray(["Aaron", "Bathsheba", "Cain"])
    var places: array(["Gomorrah", "Jerusalem", "Bethlehem"])

    with (second_name: names[1]) {
        echo("Second name: ${second_name}")

    with (first_place: places[0]) {
        echo("First place: ${first_place}")

Or, if the container isn't resizable, you can avoid a with block:

fn main() {
    range i: [0..2]
    var names: array(["Aaron", "Bathsheba", "Cain"])
    var places: array(["Gomorrah", "Jerusalem", "Bethlehem"])

    echo("Second name: ${names[i(2)]}")
    echo("First place: ${places[i(0)]})

This works because a value of type i can only ever be 0, 1, or 2. Because the sizes of names and places are fixed at 3, that means indices 0, 1 and 2 will always be valid.