Higher-level patterns

Even in a low-level language, patterns like map/reduce/fold can be expressed explicitly.

NanoLang Mascot

NanoLang also provides built-in map, filter, and reduce for arrays.

Built-in map/filter/reduce


fn double(x: int) -> int {
    return (* x 2)
}

shadow double {
    assert (== (double 3) 6)
}

fn is_even(x: int) -> bool {
    return (== (% x 2) 0)
}

shadow is_even {
    assert (is_even 4)
    assert (not (is_even 5))
}

fn sum(a: int, b: int) -> int {
    return (+ a b)
}

shadow sum {
    assert (== (sum 2 3) 5)
}

fn main() -> int {
    let xs: array<int> = (array_new 4 0)
    (array_set xs 0 1)
    (array_set xs 1 2)
    (array_set xs 2 3)
    (array_set xs 3 4)

    let doubled: array<int> = (map xs double)
    let evens: array<int> = (filter doubled is_even)
    let total: int = (reduce evens 0 sum)

    assert (== (at doubled 0) 2)
    assert (== (at doubled 3) 8)
    assert (== (array_length evens) 2)
    assert (== total 12)
    return 0
}

shadow main { assert true }

Unions and match


union SimpleResult {
    Ok { value: int },
    Err { error: string }
}

fn unwrap_or_zero(r: SimpleResult) -> int {
    let value: int = (match r {
        Ok(v) => { return v.value }
        Err(e) => { return 0 }
    })
    return value
}

shadow unwrap_or_zero {
    let ok: SimpleResult = SimpleResult.Ok { value: 7 }
    let err: SimpleResult = SimpleResult.Err { error: "nope" }
    assert (== (unwrap_or_zero ok) 7)
    assert (== (unwrap_or_zero err) 0)
}

fn main() -> int {
    let r: SimpleResult = SimpleResult.Ok { value: 42 }
    assert (== (unwrap_or_zero r) 42)
    return 0
}

shadow main { assert true }

Unsafe and extern calls

Extern functions must be called inside unsafe { ... } blocks.


extern fn get_argc() -> int

fn argc_safe() -> int {
    let mut n: int = 0
    unsafe {
        set n (get_argc)
    }
    return n
}

shadow argc_safe {
    let n: int = (argc_safe)
    assert (> n 0)
}

fn main() -> int {
    assert (> (argc_safe) 0)
    return 0
}

shadow main { assert true }

First-class functions

Functions can be passed as arguments using fn(...) -> ... types.


fn double(x: int) -> int {
    return (* x 2)
}

shadow double {
    assert (== (double 5) 10)
}

fn apply_twice(x: int, f: fn(int) -> int) -> int {
    return (f (f x))
}

shadow apply_twice {
    assert (== (apply_twice 3 double) 12)
}

fn main() -> int {
    assert (== (apply_twice 4 double) 16)
    return 0
}

shadow main { assert true }

Fold (reduce)


fn fold_sum(xs: array<int>) -> int {
    let mut acc: int = 0
    let mut i: int = 0
    while (< i (array_length xs)) {
        set acc (+ acc (at xs i))
        set i (+ i 1)
    }
    return acc
}

shadow fold_sum {
    let xs: array<int> = (array_new 4 0)
    (array_set xs 0 1)
    (array_set xs 1 2)
    (array_set xs 2 3)
    (array_set xs 3 4)
    assert (== (fold_sum xs) 10)
}

fn main() -> int {
    let xs: array<int> = (array_new 0 0)
    assert (== (fold_sum xs) 0)
    return 0
}

shadow main { assert true }

Logging, tracing, and coverage

The stdlib includes structured logging plus lightweight tracing/coverage hooks.


from "stdlib/log.nano" import log_info
from "stdlib/coverage.nano" import coverage_init, coverage_record, coverage_report
from "stdlib/coverage.nano" import trace_init, trace_record, trace_report

fn add_one(x: int) -> int {
    (trace_record "CALL" "add_one" (int_to_string x))
    (coverage_record "userguide" 1 1)
    return (+ x 1)
}

shadow add_one {
    assert (== (add_one 4) 5)
}

fn main() -> int {
    (coverage_init)
    (trace_init)
    (log_info "demo" "instrumentation demo")
    assert (== (add_one 9) 10)
    (coverage_report)
    (trace_report)
    return 0
}

shadow main { assert true }