Variables
Variables are a fundamental part of any programming language. Here's how you work with them in Ki.
TODO: Build a chart mapping literals to types
Basics
Ki requires the programmer to initialize all variables with a value, including
compound types. While this may sound onerous at first, in practice little work
is required to satisfy this requirement, and there are ways to reduce the
workload besides. For instance, types
allow default values for their fields,
and allow for static "constructor" methods (Java programmers would call them
"factory functions") to quickly utilize the most common initializations.
This requirement is found in many C style guides as well, however C is problematic in that ANSI C (C89) only accepts variable declarations at the top of functions. Consequently programmers tend to write code like this:
void safe_func_one(Enemy *e) {
Player *p = NULL;
if (!e) {
return;
}
p = enemy_get_last_target(e);
if (!p) {
return;
}
player_run(p);
}
Ki (and C99) allow the programmer to write code without a frivolous and unnecessary pre-initialization:
void safe_func_one(Enemy *e) {
if (!e) {
return;
}
Player *p = enemy_get_last_target(e);
if (!p) {
return;
}
player_run(p);
}
Or, a Ki version:
fn safe_func_one(e: &Enemy) {
# Ki doesn't allow NULL pointers, so there's no need for a check here
with (p: e.get_last_target()) {
p.run()
}
}
Declaring Variables
Variable declarations in functions look like this:
fn main() {
var name1: "Charlie"
echo("Hello {{name1}}!")
}
There's a lot packed into that single declaration, so let's break it down:
Allocation
In the above example, "name1" is allocated on the stack. Here's how to allocate it on the heap:
fn main() {
var name1: *"Charlie"
echo("Hello ${name1}!")
}
For more information on Ki's memory management, see memory model.
Types
Ki is strongly typed, but it can infer the type of a variable based on what's
assigned to it. For example, in Ki one can define a range
type, which is a
ranged integer:
range MonthOfYear [1..12]
fn main() {
var month: 12
echo("This month is number {{month}}")
}
In this small example, "12" is a potentially valid value for a host of integer
types, including our range "MonthOfYear". However, in this situation Ki will
use int
instead of MonthOfYear
as we would prefer (because the type better
conforms to its use). So let's be a little more specific:
range MonthOfYear [1..12]
fn main() {
var month: MonthOfYear(12)
echo("This month is number ${month}")
}
Some more exmaples:
fn main() {
# This is a stack variable
var age: u8(62)
# This is a unique pointer to a heap-allocated variable
var name: *string("Charlie")
# This is an atomic shared pointer to a heap-allocated variable
var celtics: $Team("Celtics")
# This is a non-atomic shared pointer to a heap-allocated variable
var pacers: ^Team("Celtics")
# This is a weak pointer to a heap-allocated variable
var also_celtics: %celtics
# This is a reference to a heap-allocated variable
var nameref: &name
}