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 anArray[String]. - To share methods between unrelated types, use a trait instead.
