← Back to the index

7. Control Flow

In this chapter

Conditional Statements (if / else)

if statements execute a block of code if a condition is true. The block is closed with the end keyword. The else branch is optional.

import console from "std/console.vv"

if x == 10
    console.print("Ten")
else
    console.print("Not ten")
end

Chained conditions are supported using else if. Multiple conditions can be checked in sequence, and only the first branch that evaluates to true will be executed.

if x == 1
    console.print("one")
else if x == 2
    console.print("two")
else
    console.print("many")
end

Blocks (begin...end)

A begin...end block groups multiple statements into a single unit. It is primarily used to create a new local scope for variables.

let x = 1
begin
    let y = 2
    console.print(x + y) // 3
end
// console.print(y) // Error: y is only visible inside the block

Loops (while)

while loops execute a block of code repeatedly as long as a condition is true. The loop is closed with the end keyword.

import console from "std/console.vv"

let i = 0
while i < 5
    console.print(i)
    i = i + 1
end

Loop Control (break, continue)

Inside a loop, you can use break to exit the loop entirely, or continue to skip to the next iteration.

let i = 0
while i < 10
    if i == 5
        break // Stop at 5
    end
    i = i + 1
end

Error Handling and Early Returns (try)

vv utilizes the std/result.vv module and a prefix try keyword for error handling. The try keyword evaluates an expression returning a result record with a type field.

import result from "std/result.vv"
import console from "std/console.vv"

fun divide(a, b)
    if b == 0
        return result.error("division by zero")
    end
    return result.ok(a / b)
end

fun calculate()
    // If divide fails, calculate() immediately returns the error.
    // If divide succeeds, val gets the unwrapped result.
    let val = try divide(10, 2)
    console.print(val) // Prints: 5
    return result.ok(val)
end

Unhandled Errors (try at Top-Level)

If an error is propagated via try and reaches the top-level of a module (outside of any function), the interpreter will stop execution and print the error content to the standard output.

import result from "std/result.vv"

fun f()
    return result.error("something went wrong")
end

fun g()
    // The try keyword here triggers an early return from g() 
    // and propagates the error from f() up to g's caller.
    let val = try f()
    return result.ok(val)
end

// try g() at the top level will cause the program to exit
// and print: "unhandled error: something went wrong"
try g()

Deferred Execution (defer)

The defer statement schedules a statement to be executed just before the current scope (block, function, or module) exits.

This is extremely useful for ensuring that resources are cleaned up properly, regardless of whether the scope exits normally or via a return statement.

The execution order of multiple defer statements is "last defer, first executed".

Because defer supports statements, you can use try to propagate errors from cleanup code, or just call functions directly to ignore their return values:

import file from "std/fs/file.vv"

let f = try file.create("temp.txt")
// close() return value is silently discarded since we're cleaning up
defer file.close(f)
import console from "std/console.vv"

let process_file = fun()
    console.print("Opening file...")
    defer console.print("Closing file...")
    
    console.print("Processing file contents...")
end

Output of the above function will be:

Opening file...
Processing file contents...
Closing file...

Defer in Loops

When used inside a loop, defer executes at the end of each iteration of the loop, not when the entire loop finishes. This is useful for cleaning up resources created within a single loop cycle.

import console from "std/console.vv"

let i = 0
while i < 3
    defer console.print("End of iteration {i}")
    console.print("Start of iteration {i}")
    i += 1
end

Output:

Start of iteration 0
End of iteration 0
Start of iteration 1
End of iteration 1
Start of iteration 2
End of iteration 2