Docs/Types

Modes

Change the behavior of an existing type.

On this page

A mode is a new name for an existing class with different methods. It adds no properties, and values convert freely between the mode and its base class.

Change how values compare#

Valk
mode CaseInsensitive for String {
    + fn equals(other: CaseInsensitive) bool $eq {
        return this.lower() == other.lower()
    }
    + fn hash() uint {
        return this.lower().hash()
    }
}

fn main() {
    let plain: String = "Ada"
    let name: CaseInsensitive = "Ada"
    println(plain == "ADA") // false
    println(name == "ADA")  // true

    let seen = HashMap[CaseInsensitive, int]{}
    seen.set("Ada", 1)
    println(seen.has("ADA")) // true
}

A mode used as a map key needs both $eq and hash, and equal values must give equal hashes. Mark these methods + so the map can call them.

Add methods and getters#

The base class's methods stay available, and the mode can add its own:

Valk
mode FileName for String {
    get extension: String {
        let dot = this.last_index_of(".") !? this.length
        return this.range(dot, this.length - dot)
    }
}

fn main() {
    let file: FileName = "report.pdf"
    println(file.extension)       // .pdf
    println(file.upper())         // REPORT.PDF
    let text: String = file       // back to the base class
    println(text.length)          // 10
}

Generic modes#

A mode can wrap a generic class:

Valk
mode Stack[T] for Array[T] {
    fn push(value: T) {
        this.append(value)
    }
    get top: ?T {
        return this.last() !? null
    }
}

fn main() {
    let stack: Stack[int] = Stack[int].new()
    stack.push(3)
    stack.push(8)
    println(stack.top ?? 0) // 8
    println(stack.length)   // 2
}

Things to know#

  • Converting between a mode and its base is implicit in both directions, so a mode is not a validation boundary.
  • When a function or operator accepts both, the type written at the call decides which version runs.
  • Containers stay distinct: Array[CaseInsensitive] is not an Array[String].
  • To share methods between unrelated types, use a trait instead.