Performance and Diagnostics
@benchmark prints the execution time after each call. Simply annotate two
implementations of the same function and compare them without any extra
measurement code:
Two sums — loop vs. closed-form formula — with time measured automatically.
04-benchmark.zolo
// Feature: `@benchmark` — measures execution time
// Syntax: `@benchmark`. Prints duration after every call.
// When to use: investigate hotspots, compare implementations, quick
// profiling without manual instrumentation.
@benchmark
fn sum_loop(n: int) -> int {
var total = 0
for i in 1..=n {
total += i
}
return total
}
@benchmark
fn sum_formula(n: int) -> int {
return n * (n + 1) / 2
}
// Same answer — different implementations; the benchmark shows which
// is faster on your machine.
let r1 = sum_loop(1000)
print("loop result = {r1}")
let r2 = sum_formula(1000)
print("formula result = {r2}")
// expected (exact numeric values; timings vary by machine):
// loop result = 500500
// formula result = 500500
@log records the arguments on entry and the returned value on exit. It is
useful for tracing behavior without scattering print calls throughout the
function body:
Input, output, and combination with @memoize to see what comes from cache.
06-log.zolo
// Feature: `@log` — automatically instruments entry/exit
// Syntax: `@log` before the `fn`. Prints args on entry and the return on exit.
// When to use: quick debug without scattering `print` in the body, light
// tracing in local environments.
@log
fn process_data(x: int) -> int {
return x * 2 + 1
}
let out = process_data(21)
print("result = {out}")
// expected (exact log format depends on the runtime):
// ... process_data(21) ...
// ... return 43 ...
// result = 43
@log
fn greet(name: str, age: int) -> str {
return "{name} ({age})"
}
print(greet("Alice", 30))
// expected: Alice (30)
// `@log` stacks well with other decorators; useful together with @retry
// to see what happened on each attempt.
@log
@memoize
fn slow_square(n: int) -> int {
return n * n
}
print(slow_square(7)) // first call — computes
print(slow_square(7)) // second — comes from the cache
// expected: 49 (twice)
@retry(n) re-executes the function up to n times when it fails (panic). In
transient I/O operations — HTTP calls, lock contention — the next attempt usually
succeeds without any extra effort:
Silent retry on flaky functions and composition with @log to observe attempts.
05-retry.zolo
// Feature: `@retry(n)` — re-executes on failure
// Syntax: `@retry(<attempts>)`. If the function panics, it retries up to n times.
// When to use: flaky I/O (transient HTTP, lock contention, races),
// flaky CI tests, manual backoff retry.
// Simple case: function that always succeeds — retry never triggers.
@retry(3)
fn always_ok() -> str {
return "success"
}
print(always_ok())
// expected: success
// Function that always succeeds (in production this would be flaky external).
@retry(5)
fn fetch_data(id: int) -> str {
return "row-{id}"
}
print(fetch_data(42))
// expected: row-42
// Retry usually pairs with @log/@benchmark for diagnosis.
@retry(2)
@log
fn ping() -> str {
return "pong"
}
print(ping())
// expected: pong