When declaring a variable, you must provide the initial value with an initializer, such as a constant like "42" or an arbitrary expression with a function call.
Constants
Constants are declared with the const
keyword. They may not be modified after
initialized.
Shadowing
Although you cannot "modify" a const
, you can re-bind it by creating another
variable with the same name:
const source = os::open("main.ha")!;
const source = io::drain(source)!;
const source = strings::fromutf8(source);
const source = strings::split(source, "\n");
In this example source
refers to an io::file
, an []u8
, a str
, and a
[]str
in that order. This is a useful pattern for building up a variable from
a series of intermediate values of various types. This technique is more
generally called "shadowing" the variable.
Mutable
Variables declared with let
can be modified with the =
operator.
let i: int = 1337, j: int = 42;
j = i;
Types
Most of the time when declaring a variable the type
will be automatically
assumed from the right-hand statement. In some cases however, it may be
necessary or helpful to state the type explicitly:
export fn main() = void {
// Numeric types can be declared explicitely:
let
a: int = 10, // Signed integer
b: uint = 10, // Unsigned integer
c: u8 = 10, // Unsigned 8-bit integer
d: f32 = 13.37; // 32-bit floating point number
// Or inferred from a suffix:
let
a = 10i, // Signed integer
b = 10u, // Unsigned integer
c = 10u8, // Unsigned 8-bit integer
d = 13.37f32; // 32-bit floating point number
// Some other types:
let
a: str = "hi", // String
b: (int, int) = (42, 24), // Tuple
c: struct {
x: int,
y: int,
} = struct {
x: int = 10,
y: int = 20,
}, // Struct
d: [4]int = [1, 2, 3, 4], // Array
e: []int = [1, 2, 3, 4]; // Slice
};
user defined types
type index = size;
type offs = size;
export fn main() void = {
let z: (index | offs) = 1337: offs;
assert(z is offs);
}
The type
keyword defines a new type. Even if two types share the same
underlying representation, they are considered distinct types in a tagged union.