Variables & Types

Toka is a statically-typed language with a rich type system. Let's explore how variables and types work.

Variable Declaration

Use the auto keyword to declare variables:

auto x = 10          // i32 (default integer type)
auto y = 3.14        // f64 (default float type)
auto name = "Toka"   // str (string slice)

Naming Rules & Hyphen (-) Resolution

Toka has a strict and elegant rule regarding the hyphen (-) character to achieve both modular naming convenience and unambiguous mathematical syntax:

Package Names & Namespaces

Filesystem path segments in an import may use hyphens. Names that enter Toka's semantic namespace—including an import alias and the namespace used at a call site—must be ordinary identifiers, so use an underscore alias for a kebab-case path:

import std/io::println

// The path may contain hyphens; the alias may not.
import ./nested/toka-ink as toka_ink

fn main() -> i32 {
    toka_ink::render()
    return 0
}

Variables & Functions

Ordinary variable names, constant names, shape/struct names, and function definitions cannot contain hyphens. They must follow standard alphanumeric/underscore naming rules (such as camelCase or snake_case):

fn main() {
    auto max_size = 1024       // OK (snake_case)
    auto maxSize = 1024        // OK (camelCase)
    // auto max-size = 1024    // Error: Hyphens are not allowed in variable names
}

Unambiguous Subtraction

Because hyphens are strictly forbidden in variable and function names, the subtraction operator (-) is unambiguous. For readability and to keep the lexer boundary explicit, binary subtraction must be surrounded by spaces:

import std/io::println

fn main() -> i32 {
    auto sub1 = 10
    auto sub2 = 3
    
    auto result = sub1 - sub2
    auto value = 10 - 3
    println("Result: {}, Value: {}", result, value)
    return 0
}

Explicit Type Annotations

auto always infers the binding type and cannot carry a declaration-side type annotation. To constrain inference, ascribe the initializer with : Type:

auto x = 10:u64
auto y = 3.14:f32
auto flag = true:bool

Absence Is Not One Type

Toka 1.0 keeps three absence domains distinct: a nullable handle uses nul *T, nul ^T, or nul ~T and the value null; a nullable payload uses T? and none; and an optional operation result uses Option<T>::None. These forms do not implicitly convert into one another. Use guard or ?? for the selected nullable layer, and use postfix ! only to propagate Option or Result.

Primitive Types

Toka's type system is defined in lib/core/types.tk and includes:

TypeDescriptionSize
i8, i16, i32, i64Signed integers1-8 bytes
u8, u16, u32, u64Unsigned integers1-8 bytes
f32, f64Floating point (IEEE 754)4-8 bytes
boolBoolean (true / false)1 byte
charC-style character (i8)1 byte
byteRaw byte (u8)1 byte

Platform-Dependent Types

pub alias usize = u64   // Pointer-sized unsigned integer
pub alias isize = i64   // Pointer-sized signed integer

On 64-bit systems, usize is u64; on 32-bit systems, it would be u32.

Type Aliases

Toka supports two kinds of type aliases:

Weak alias — Semantically identical, transparent to the compiler:

pub alias usize = u64

Strong type — Same memory layout, but requires explicit conversion:

pub type Addr = u64
pub type OAddr = u64

Constants

Use const for compile-time constant values:

pub const MAX_SIZE = 1024:u64
pub const NAME = "Toka"

Constants are inlined at compile time — no memory is allocated and their address cannot be taken.

Mutability

Variables are immutable by default. Use the # suffix to make them mutable. The # token is only needed in two places:

  1. At declaration — to mark the variable as mutable
  2. Before a mutable method call — to present the guard on the variable
import std/io::println

pub shape List(data: i32)
impl List {
    pub fn sort(self#) {}
    pub fn push(self#, val: i32) {}
}

fn main() -> i32 {
    auto y = List(data = 10)  // immutable
    auto x# = List(data = 10) // mutable — # at declaration

    // Reading and assignment don't need #
    x = List(data = 20)       // OK — plain assignment
    println("{}", x.data)     // OK — plain read

    // Calling a mutable method requires # on the variable
    x#.sort()                 // OK — # on variable before .method()
    x#.push(5)                // OK
    return 0
}

This is an example of Toka's Attribute Token System — the # suffix denotes soul mutability and is placed on the variable itself when declaring it or calling its mutable methods.