Decorators

Decorators attach to function declarations. Trigger decorators schedule when the function runs. Wrapping decorators intercept every call and delegate to the original.

Trigger decorators

A trigger decorator answers "what schedules this function?" The runtime fires the body without a direct caller.

-- lifecycle
@on_start fn boot() { setup_things() }
@on_exit  fn cleanup() { close_handles() }
@on_panic fn record() { telemetry.flush() }

-- schedule
@every(1s)            fn tick() { metrics.flush() }
@cron("0 * * * *")   fn hourly() { rotate_logs() }
@delayed(500ms)       fn warmup() { prefetch() }

-- file watching
@watch("./config.toml") fn config_changed() { config.reload() }

-- OS signals
@on_signal("INT") fn graceful() { state = "shutting_down" }

The runtime stays alive while any persistent trigger is registered (@every, @cron, @on_signal, @watch). Once all have fired or been quiesced by @once, the process exits naturally.

-- @once: fire exactly once, then stop
@once @every(5s) fn one_shot() { do_thing() }
-- equivalent to: fire after 5s, then don't reschedule

Trigger-decorated functions must not take regular parameters. The runtime calls them with no arguments.

Wrapping decorators

A wrapping decorator answers "what happens around every call?" The bound name becomes a dispatcher that intercepts the call.

scratch.xs

@memoize caches results by a key derived from the argument values. Recursive calls hit the same cache, so fib(30) only runs the body 31 times instead of exponentially many.

-- @retry(n): run up to n attempts, swallow thrown exceptions
@retry(5) fn fetch(url) { http.get(url) }

-- @trace: print call name + args before, return value after (stderr)
@trace fn handle(req) { process(req) }

-- @timed: print elapsed milliseconds after each call
@timed fn build_index() { index_files() }

All wrapping decorators pass arguments through unchanged and return the body's result. @retry re-raises the final exception if every attempt fails, so the caller's try/catch still sees it.

Composing decorators

Multiple decorators on the same function stack in declaration order, outermost first:

scratch.xs