Variables
Variable blocks define variables; this is their definition:
variable {
name: string
mutable:
heap:
type:
value: {value initializer}
}
A string
variable looks like this:
variable {
name: "name"
mutable: false
heap: false
type: string
value: "Charlie"
}
You won't have to type all that out; Ki supplies a shorthand for variables:
fn main() {
val name: "Charlie"
}
Ki has type inference, so it can figure out what type a variable is by looking
at its initializer and its use. In this case, Ki knows that "name" is a
string
, because its initializer is a string literal.
If you'd rather not rely on that, you can specify the type manually. Primitives take a single value, so they're easy:
fn main() {
val name1: "Charlie"
val name2: "Charlie"
}
Besides type, "name" has two other attributes. First, it is allocated on the
stack. If you want something allocated on the heap, just use the heap
keyword:
fn main() {
*name1: StringBuffer {
value: "Charlie",
alloc: 64
}
*name2: StringBuffer("Charlie", 64)
}
Note that we switched from a string
to a StringBuffer
here. Rarely will
you want to allocate a primitive on the heap.
The other attribute is mutability. Variables in Ki are immutable by default;
once you initialize a variable, you can't assign something else to it unless
you've declared it mutable. Furthermore, even if a variable is a compound type
with mutable fields, those fields can't be changed unless the variable is
marked mutable as well. To mark a variable as mutable, use the var
keyword:
fn main() {
!name1: StringBuffer {
value: "Charlie"
alloc: 64
}
!name2: StringBuffer("Charlie", 64)
}
If you want both heap allocation and mutability, use both:
fn main() {
*!name1: StringBuffer { "Charlie", 64 }
*!name2: StringBuffer(value: "Charlie", alloc: 64)
}
Note that order is important. !*
won't work.
We showed a little with StringBuffer, but here is more of how variable declaration works for compound types:
type Person {
:fields {
*first_name: ""
*last_name: ""
*favorite_color: ""
age: uint(0)
}
}
fn main() {
charlie1: Person()
charlie2: Person {}
charlie3: Person {
first_name: *"Charlie"
last_name: *"Gunyon"
favorite_color: *"green"
}
charlie4: !Person {*"Charlie", *"Gunyon", *"green", 32}
charlie5: !Person(*"Charlie", *"Gunyon", *"green", 32)
charlie6: *!Person {
first_name: *"Charlie"
last_name: *"Gunyon"
favorite_color: *"green"
age: 32
}
charlie7: *!Person {*"Charlie", *"Gunyon", *"green", 32}
charlie8: *!Person(*"Charlie", *"Gunyon", *"green", 32)
}
Some things worth mentioning here:
- Variables in Ki must be initialized, and this extends to type fields as well.
- "Person" provides default values, which is how "charlie1" and "charlie2" can provide no initialization, and how "charlie3" can omit "age" from initialization. It is highly recommended that all fields initialize to a "zero" or "empty" value; this makes it very easy to work with higher level data structures, for example.
- You can use parentheses if everything fits on the same line.
- If you don't use named parameters, you must specify parameters in order.