Language
Modules (use / export)
Split code across files. export makes a task/type/let/enum/routes public; anything else stays private. A module is a map of its exports, imported under an alias.
The module — mathlib.syn:
export task square(n)
give n * n
export let VERSION be "1.0"
task private_helper() -- no `export` → not visible to importers
give 42
The entry that imports it:
-- Doc example: modules (use / export). Imports ./mathlib.syn under the alias `math`.
intent: "doc example: modules"
use "./mathlib.syn" as math
print("math.square(5) = " + text(math.square(5)) + ", VERSION " + math.VERSION)
test "import an exported task and let (private_helper is NOT visible)"
assert_eq(math.square(5), 25)
assert_eq(math.VERSION, "1.0")Rules§
use "./path.syn" as alias— paths are relative to the importing file;.synonly, no URLs/FFI, and traversal (../) is blocked.- Cross-file calls need the alias prefix:
math.square(5),math.VERSION. - Imports are cached (loaded once), transitive, and cycle-checked.
- A module must not have a top-level
requireorserve— those belong in the entry file. A per-taskrequireinside a module task is fine. synsema check entry.synresolves and parses the whole import graph (broken paths, cycles, forbiddenserve/requirein modules all fail the check).
export routes — route groups a serve can mount§
A big site doesn't have to be one big serve block. A module exports a routes group; the entry mounts it (bodies can call the module's private helpers by simple name):
-- shop.syn
task fmt(n) -- private
give "$" + text(n)
export routes shop
route "GET /shop"
give html("<h1>" + fmt(99) + "</h1>")
route "POST /shop/buy"
expect body {item: text}
give created(json of request)
-- app.syn
use "./shop.syn" as shop
serve on 8080
mount shop.shop -- or: mount shop.shop at "/store"
Groups accept only route entries (no stream, no per-route rate_limit — clear errors); a mounted requires auth still demands auth with on the serve block, validated when the serve is built. See HTTP server and Build a website.