Attributes

Every attribute the language defines. Any other @word is allowed wherever attributes are, but it's inert metadata - nothing looks at it. For the attribute syntax itself and where attributes can be placed, see Syntax Reference.

Entry points and tests

@main

#main
function

Marks the start function - the one a folder runs as its program entry point (--run, see Getting Started). Only valid on functions. At most one per folder - a second @main in the same folder is an error on both, not a silent override. It takes no parameters, or one whose type is an @args struct - then the command line's flags are read into it before the body runs.

@args(usage: String = "", about: String = "", autoShort: Boolean = true)

#args
struct

Makes the struct's fields the program's command-line flags. Each field is a flag named after it - maxConnections is --max-connections - read as the type it's declared with, so a port that doesn't fit a U16 is an error naming the flag. A field keeps its default when its flag isn't given; one without a default that isn't optional (T?) has to be. A field named rest takes the arguments that aren't flags, and one named passed what follows --. about goes before the usage line in the help. A field gets its first letter as a short flag when no other field starts with it - unless autoShort is false, and then only @arg's letters are short flags. The struct gets parse(args), help() - usage, then a line per flag with its type, its @arg description and its default - and fromCommandLine(), which a @main taking the struct as its parameter calls: --help - and -h, when no field takes h - prints the help and ends the program, and a flag that's wrong prints what's wrong and the help to &stderr and ends it with exit code 2. It's a builtin attribute macro, calling the library's Arguments - see Command-line Programs.

@arg(description: String, short: String? = undefined, alone: Unknown = undefined, separator: String = "")

#arg
field of an @args struct

Says what an @args struct's field is for: description is its line in the help. short is a one-letter flag for it, -v, in place of the usual one - a field's first letter, when no other field starts with it - and "" gives it none. With alone, the flag can be given without a value, and the field is then alone; it takes a value only after =, so --test and --test=e2e both work and the word after it is never its value. A list field with a separator splits its value on it, each piece trimmed: --test=e2e, service.

@test(..stages)

#test
function

Marks a function as a test (see Testing). Any number are allowed, anywhere in the project. A test fails when it returns an error value or stops on an error, and passes otherwise, and its function's name - often a sentence, in backticks - names it in the report. stages are optional names - plain @test works too - for the kinds of test it is, like @test(e2e, service). A test without any always runs; one with some runs only when --test=e2e names one of them, or with --test-all.

@testHelper(displayName: String)

#testhelper
function

Marks a function as test-only support code. It isn't run as a test itself, and referencing it from anything other than a @test, @bench or @testHelper function is a warning. displayName is optional - plain @testHelper works too.

@bench(displayName: String, ..stages)

#bench
function

Marks a function as a benchmark, timed by --bench rather than run as a test (see Benchmarking). It takes no arguments and isn't async. It's called over and over - after a warm-up, in batches sized to the time one call takes, for about a second - and reported as the time one call takes: the median batch's, with the fastest and slowest. It fails like a test when it returns an error value. displayName is optional - plain @bench works too - and names it in the report. stages work as @test's do: a benchmark without any is always timed, and one with some only when --bench=slow names one of them, or with --bench-all. --bench-whole times the tests those flags pick as well.

Context

@inject

#inject
definition

Injects an instance into every call in this file - the default level of the three (file, folder, project). A single one per file. On a const the value is the const's; on a function, the function itself, so &name(...) calls whichever one is injected - the default, or a caller's own. Read back with a Context Reference, &name - or, for a backtick-quoted name, &`the name`. The value is made the first time something reads it, and is the same one every time after - so a test that injects its own never makes it.

@injectLocally

#injectlocally
definition

Injects an instance into every call in this folder - the local level. A single one per folder.

@injectGlobally

#injectglobally
definition

Injects an instance into every call in the project - the global level. A single one per project.

@require(&name) | @require(&name: Type) | @require(&name := default) | @require(&name: Type := default) | @require(CONST) | @require(&name, CONST, ...)

#require
function, parameter, field or interface method

Declares a context value the function reads as &name, with its type and, optionally, a fallback used when nothing further up the call stack injected name (see Context). The body still reads it as &name - it isn't an argument. Each entry is written like a parameter, its name with the & it's read with - &name, &name: Type, &name := default or &name: Type := default - and one @require takes as many as the function reads: @require(&stdout, &clock, &mailer: Mailer := defaultMailer). A name without the & is a global const instead (below); default, when given, must be of its type.

The type goes without saying when it's declared. @require(&stdout) alone takes the type &name is declared with where the function is: a builtin's (&stdout is an interface.OutputStream, &fs an interface.FileSystem - see Context), or the context name: Type declaration - or @inject definition - reaching the function's file. A name nothing declares there needs its type written. Writing a declared one's own type anyway is warned about - @require(&stdout) says the same - and a type that differs is the function's own, as it always was. A default needs no type of its own either - @require(&stdout := bufferedOutput). Without one, a call where nothing injected name reads no value, the same as any context value nobody injected. A backtick-quoted name works too - @require(&`shop name`: String), read as &`shop name` - but never a path.

Every function declares what it needs. A function that reads a context value - itself, or through a function it calls - where nothing injected it is warned about until it @requires it. Calls are followed through the program's own functions and methods, so f calling g that reads &mailer needs @require(&mailer) too, unless f injects mailer before the call or g's own @require gives a default, which it then provides itself. The builtin values count - &stdout, &fs, &clock, ... - but not the policies (&hashSeed and the others), which builtin operations read on their own. A function only passed as a value isn't called where it's passed, and @main, @test and @bench functions - called by the runtime, with the builtins provided - declare nothing.

An effect that names it requires it. @reads(&clock), @writes(&cart) or @effects(&stdout) already says the function reaches that context value, so it counts as @require(&clock) - with the type the value is declared with and no default - and needs no @require of its own. A bare @reads names nothing and requires nothing; a value that needs another type or a default still takes its @require.

Global consts are named without the &. A function that reads a global const - const MAX_USERS := 100 - names it in its @require by its name alone: @require(&stdout, MAX_USERS). Reading one that isn't named is warned about, so the attributes say everything a function takes from outside itself. Only the function that reads it names it - a const can't be injected, so there's nothing for a caller to provide, and its callers needn't name it. A lambda counts for the function it's written in; a const's own value, a field's default and the like aren't in a function and name nothing. A const holding a type - const Small := U8 | U16 - isn't a value, and isn't named. An entry without the & that names no const is an error - @require(stdout) is told to be @require(&stdout).

On what holds a function. Written on a parameter, a field or an interface's method, @require says what calling the function held there may read. Calling it - run(), job.run(), counter.count() through the interface - needs those values where it's called, like calling a function that reads them; a lambda given to that parameter or field may read them without the function it's written in needing them, since whatever calls it provides them.

Instances

@singleton

#singleton
struct

Every instance is the same instance - constructing one again gives back the one that already exists.

Functions

@inline

#inline
function, method, lambda or block

Inlines the function, method, lambda or block at every use.

@pure

#pure
function, method or interface member

Declares that the function's result depends only on its arguments, and that it changes nothing they hold - the weakest of the four effect levels, @pure < @reads < @writes < @effects. The checker works out every function's effects from its body and what it calls, written or not; one marked weaker than that is warned about, naming what it does and through which call - the program still compiles and runs. On a @memoize function it's an error, since results kept for a function that changes things would be wrong. Calling a function it's given as a parameter doesn't count - what passes it pays for it - so apply(f, x) can be @pure. On an interface member it binds every implementation. @memoize is @pure too.

@reads(..contexts)

#reads
function, method or interface member

Declares that the function may also read context values - a weaker promise than @pure, a stronger one than @writes. With targets, only those context values: @reads(&clock). Plain @reads allows any. Reading a policy (&hashSeed ...) or a value injected in the function itself doesn't count. Each context value named is required too - @reads(&clock) stands for @require(&clock) as well (see @require); the same goes for @writes and @effects. Checked against what the function does - see @pure.

@writes(..targets)

#writes
function, method or interface member

Declares that the function may also change state that outlives the call: what a parameter holds - self included - or what a context value holds. A target names which: a parameter's name, or &name for a context value - @writes(self, &cache). Plain @writes allows any. Setting a local doesn't count; setting a field of one does when it came from a parameter or a context value. Reads are allowed. Checked against what the function does - see @pure.

@effects(..contexts)

#effects
function, method or interface member

Declares that the function may also do I/O - write to the terminal or a file, read input, wait, talk to the network. With targets, only through those context values: @effects(&stdout). Plain @effects allows any - and is what calling a function value that isn't a parameter counts as, since nothing says what it does. Injecting a value doesn't remove an effect.

@slow

#slow
function or method

Marks the function as expensive to call - a hint for the optimizer, which never inlines it or computes a call to it twice, and moves a call of a @pure one out of a loop whose iterations give it the same arguments. It's also a good candidate for @memoize, or for calling in #comptime(...). Can't be used together with @inline.

@memoize

#memoize
function

Caches the function's results by its arguments. Implies @pure - unless another level is written with it, e.g. @effects(&stdout) on one that logs each time it really runs.

@async

#async
function or method

Makes the function run as a task on the event loop. It's declared as returning Promise<Result>, and its returns give a Result. Calling it runs the body at once, up to its first await, then hands back the Promise so the caller can carry on - everything runs on one thread, one task at a time. await is only allowed inside an @async body, and a generator can't be @async. @main, @test and routes can be: the program, the test or the request waits for the promise to settle. See Async.

@parallel(unsafe?)

#parallel
function

Makes the function run on another thread. Like @async, it's declared as returning Promise<Result> and its returns give a Result - but calling it hands the work to one of the host's threads and gives back the Promise at once, so several calls run at the same time, one per core. Its arguments are copied to the thread and its result copied back, so neither can hold a function, a Promise, an Iterator, a JsValue, a Regex or a Pointer. It has to be self-contained: it reads no context value it doesn't inject itself, does no I/O but through those, and changes nothing it's given - the checker says when it isn't. It can't await, and it doesn't go with @async, @memoize, @inline, @js or an entry point's attribute. A builtin attribute macro: expanded, it's a call of Promise.parallel with a local function holding the body. Where there are no threads - a WASI runtime, a native executable, a web page - it runs at once and gives a settled promise, with the same result. @parallel(unsafe) checks none of that: the work gets copies of the caller's context values, does any I/O, and shares state through a SharedMap, a Channel or a SharedCounter - see Unsafe threads. See Async.

@deprecated(message: String)

#deprecated
definition

Marks a definition as deprecated - using it is a warning, showing message when one is given. message is optional - plain @deprecated works too.

@native

#native
function or struct, in the standard library

Marks what belongs to the runtime itself - only the standard library has any. On a function: one the runtime provides, declared with no body so the type checker and the editor know its parameters and result (the WASM library's natives.rfn) - a call to it is the runtime's own operation, never a call into Refingere code, and two folders may each declare the same one without clashing. On a struct or enum named like a builtin type: the library's layout of that type - @native struct Range { ... } is how a Range is held, so a value of either is a value of the other.

Interfaces

@derive(interfaces: Array<Interface>)

#derive
struct or enum

Generates implementations of the listed interfaces, e.g. @derive(Equatable, Hashable, Clone) - see the builtin interfaces.

@unit @unit(definition: Unit, offset: F64 = 0.0)

#unit
empty struct

Makes the struct a unit a Quantity is measured in. Bare, it's a base unit - a dimension of its own, as a struct with no @unit is too. With a definition, it's made of other units: a number times units multiplied, divided and raised to whole powers. Quantities whose units come to the same are one type, and as converts between units of one dimension. offset is where the unit's zero is, for temperatures. The language's own units - Kilometer, Newton, Celsius, Byte and more - are made the same way, each in #builtin.unit by what it measures (#builtin.unit.length.Kilometer), brought out with a type alias; see Quantity and the units listed after it.

HTTP

@serve(port: U16 = 8080, host: String = "localhost")

#serve
function

Turns the function into an HTTP server. Calling it runs its body first, then listens on host:port and answers every request with a route - a function marked @route or one of its shorthands - in this folder or any folder inside it (one in a folder inside it is global, for the server to reach it). It's a builtin attribute macro: the function ends with #builtin.serve(...), which starts the server, and a @js function beside it, <name>Requests, answers each request with #builtin.answer and every route by method and path - expanding it in place writes it all out (see Servers and Browser Apps). Each request calls its route like any other call, so an inject in the body doesn't reach the routes - it ends with the body's block, before the first request. State every route shares - a database, a store - is a definition marked @injectLocally (or @inject / @injectGlobally), which every call in the folder sees as &name. It's usually written next to @main. A request no route's path matches gets a 404, and one whose path matches but whose method doesn't gets a 405. The server is the JavaScript host's - Bun's, which --run uses - so a server needs one.

@route(method: String, path: String)

#route
function

Makes the function the handler for requests with this method ("GET", "POST", ...) and path, served by the folder's @serve function. A route takes no parameters and answers with a Response - a status, headers and a body - or Error ! Response.

The request is &request, which the server injects before calling the route: a {name} segment of the path is in &request.params, and the query string, a form's fields, the headers (named in lower case) and the body are there too, all as text - the route parses what it needs.

The response is built as a struct, Response{status := 404, body := "..."}, or with one of Response's functions, which set the Content-Type: Response.json, html, css, text, bytes, redirect and empty. An error the route ends with answers 500, with its message as the body.

A route is still an ordinary function, so a test injects a Request of its own and calls it without a server.

@get(path) @post(path) @put(path) @patch(path) @delete(path)

#http-methods
function

Shorthands for @route with the method in the name: @get("/todos") is @route("GET", "/todos"). path is a String. Any other method still needs @route, e.g. @route("OPTIONS", "/todos").

@entity(table: String = "")

#entity
struct

Makes the struct a row of an SQL table - table, or the struct's own name when it's left out - each field a column of the same name. An #SQL SELECT that reads only that table and selects every field that isn't optional - *, or each column by name, t.id too - is a Query<Self>: run() gives a Table<Self> and first() a Self?, each row decoded into the struct as it's read, a Boolean field from the 0 or 1 SQLite keeps it as. A column the query selects that isn't a field is an error when the program compiles. A query that selects anything else - an expression, a column renamed with AS, a join - keeps its rows as Maps of their columns, unless it names its own row type: #SQL<Row> {{{ ... }}}, for any struct.

The table's functions. It's a builtin attribute macro: the struct gets, each through &database, migrate() - CREATE TABLE IF NOT EXISTS, a column per field - insert(title := "Milk"), the row added, with each field left out its default (a λ default is called) and an autoincrement key the database's; all(), first(), last(), count(), where("done = ?", [true]) - a WHERE clause, its ? bound - deleteAll(), drop() and table(). With a key - the field marked @sqlid, or else one named id - also select(id), exists(id), delete(id), update(id, done := true) - the fields given changed, the row as it is then - and save(todo), the whole row written, added when there's none. A field that isn't a number, Boolean, String or Bytes is kept as its text, and read back from it.

@sqlid(autoincrement: Boolean = false)

#sqlid
field of an @entity struct

Makes the field its @entity struct's key: the column its rows are found by - select, exists, delete, update and save take it - and its table's PRIMARY KEY. With autoincrement, it's an integer the database numbers: insert leaves it out and gives back the row with it. Without @sqlid, a field named id is the key.

Browser

@app(mount: String = "#app")

#app
function

Makes the function the entry point of a browser app: the folder is built for the browser instead of run as a program, and once the page loads, the function is called and what it returns - Html, usually with components inside - replaces the content of the element mount selects. The page itself only needs that element and the built script. At most one per folder, like @main, and a folder has one or the other. In an app, context values like &clock and &random come from the browser; there's no &fs.

@component

#component
struct

Makes the struct a component: a piece of the page with its own state. It needs a render method returning Html. When an instance is put into #HTML with #{ }, it's rendered there, and rendered again - just that part of the page - whenever one of its @state fields changes. Its other fields are fixed once it's constructed, so they're how a parent passes it settings: Counter{step := 5}. render is an ordinary method too, so a test can call it and read the result with toString().

@state

#state
field of a @component struct

Marks a field of a component as its state: setting it renders the component again. Several changes in the same event handler cause a single render, after the handler returns. Fields without @state can't be set after construction.

Macros

@bodyMacro @stringMacro @deriveMacro @attributeMacro @programMacro

#macro-kinds
implement macro

Says what kind of macro an implement macro block is, and so how it's used - exactly one per macro. @bodyMacro is used as #name {{{ ... }}} and gets the body; @stringMacro as #name"..." and gets the string; @deriveMacro as @derive(Name) on a struct or enum, gets its definition and returns implement blocks to add; @attributeMacro as @name(...) on a definition, gets the definition and its own arguments, and returns the definition to use in its place; @programMacro is never used by name - it runs once over the whole program, selects what it wants with program.find(...), and returns code to add. See Writing macros.

@phase(phase: CompilePhase)

#phase
implement macro

The stage of compilation a macro runs at: CompilePhase.Lex (only tokens), Parse (a syntax tree, names not resolved), Analyze (every name resolved across the program - what #comptime.definitionsWith needs) or Typecheck (every expression's type known). Optional: without it, a macro runs at the earliest phase its input exists at - Lex for a String or Array<Token>, Parse for a syntax tree - and derive, attribute and program macros at Analyze.

JavaScript

@js(module: String, name: String?) | @js(name: String?)

#js
function

Joins a function to JavaScript and TypeScript, one way or the other depending on whether it has a body. On a declaration - a function with no body - it's a JavaScript function Refingere calls: module is a path relative to the file ("./money.ts") or a package name ("node:path"), and name the export it calls - the function's own name when left out. On a definition - a function with a body - it's a Refingere function JavaScript calls: a module compiled with --js comes with a loader whose load() gives an object with a method per such function, under name or its own name, and a .d.mts with their types. Values cross as described in JavaScript and TypeScript: a thrown exception comes back as an Error, and a function that ends with an error throws it. Without a JavaScript host - a module compiled without --js - a declaration panics when it's called, and a definition is an ordinary function.

Lifecycle

@onError @onYield @onReturn @onBlockEnd

#defer-semantics
Defer statement

Chooses when a Defer runs: when the block exits with an error (@onError), each time a generator pauses at a yield (@onYield), on a return (@onReturn), or whenever the block ends (@onBlockEnd).