Docs/Language fundamentals

Values & types

Declare variables, convert values, and represent missing data.

On this page

Valk checks types at compile time. You can let it infer local types from values while keeping function inputs and outputs explicit.

Declare and update a variable#

Valk
fn main() {
    let quantity = 3
    let price: float = 4.5
    quantity += 2
    println("Items: %quantity")
    println("Total: %{quantity * price}")
}

quantity is inferred as int. price: float supplies an explicit type. A variable declared with let can be reassigned; its type stays fixed. Use %{expression} to insert an expression into a string.

Choose a number type#

Type Use
int, uint Signed or unsigned integers sized for the compilation target
i8, i16, i32, i64 Signed integers with an explicit width
u8, u16, u32, u64 Unsigned integers with an explicit width
float Floating-point numbers sized for the target
f32, f64 Floating-point numbers with an explicit width
bool true or false

Use explicit widths for binary formats and native interfaces. Integer types expose their bounds through T.$min and T.$max, such as u8.$max for 255.

Convert and parse#

Use .to(Type) when a conversion should be explicit. Parsing text can fail, so decide how the program should handle invalid input:

Valk
fn main() {
    let count = 12
    let text = count.to(String)
    let parsed = "42".to(int) !? 0
    let fallback = "unknown".to(int) !? 0
    println("%text, %parsed, %fallback")
}

The !? 0 supplies zero if parsing fails. Text outside the destination integer's range also produces a parsing error. For a user-facing input form, handle the error and explain what needs correcting instead of silently substituting a value.

Represent an optional value#

A ? before a type makes it nullable. Check it before using the underlying value, or provide a default with ??:

Valk
fn display_name(name: ?String) String {
    if isset(name) {
        return name
    }
    return "Guest"
}

fn main() {
    let name: ?String = null
    println(display_name(name))
    println(name ?? "Anonymous")
}

isset narrows the value to its non-null type inside the branch. An early return works too: after if !isset(name) : return, the rest of the function can use name as a String.

Keep the two fallback operators distinct: ?? handles a null value; !? handles an error from a call.

Use the surrounding type#

When the destination already tells Valk the type, .{ ... } can omit a repeated type name:

Valk
let scores: Array[int] = .{ 10, 20, 30 }

This also works in typed function arguments and class properties. Spell out the type when it makes an example easier to understand.