---
name: rontolisp
version: 0.1.625
description: >-
  Write, run and debug rontolisp programs -- the Common Lisp subset that runs
  identically on an interpreter, the JVM and two WebAssembly backends. Use this
  whenever you touch a .lisp or .asd file in a rontolisp project, whenever the
  user says rontolisp, and whenever you are about to write Common Lisp that will
  run under rontolisp -- even if the request never uses the word "skill" or names
  a backend. Your Common Lisp knowledge is a PRIOR here, not the truth: this
  skill carries the delta -- exactly which operators exist, which CL features are
  missing or partial, the rontolisp-only extensions (fetch, async/await,
  linalg/vec, java:, wit-import, wasm-export, ql:quickload), and how to actually
  run a program on each backend.
---

# rontolisp

rontolisp is a Common Lisp subset with four backends that must agree: the
interpreter, a JVM bytecode compiler (`-o Prog.class`), and two WebAssembly
compilers (wasm-GC by default, `--no-gc` for a linear-memory module). Programs
are ordinary `.lisp` files and systems are ordinary `.asd` files, so a real
Common Lisp library often loads verbatim -- and so does a real Common Lisp
mistake.

This bundle is generated from the rontolisp documentation
(668 functions, 105 macros, 29 special forms); every file under `references/` is the same text published
at https://making.github.io/rontolisp/docs/en/. Skill version 0.1.625.

## How to work in this language

Your Common Lisp knowledge is right about most of the core and wrong in
specific, recurring places. It fails in exactly two ways, and both are cheap to
check before you write the code rather than after the error:

1. **Reaching for an operator that is not there.** rontolisp ships a fixed set.
   Before using an operator you have not already seen working in this project,
   look it up in `references/operators.md` -- every name it has, by category. Not
   listed means not there: reshape the code, or write the helper yourself. When
   you can run code, `(rontolisp:list-functions)`, `(rontolisp:list-macros)` and
   `(rontolisp:list-special-forms)` answer the same question at runtime.
2. **Assuming full CL semantics behind a name that IS there.** `loop`, `format`,
   `defpackage`, CLOS, `values`, string mutation and non-local exits all exist
   and all stop short of the standard somewhere. The whole delta is inlined
   below -- read it before you lean on any of them.

Then run the program. An implementation with four backends is not a place to
reason about output; a program that has not been executed at least on the
interpreter is not finished. If it is meant to be compiled, run it on the
backend it targets, because that is where the compiled-path restrictions bite
(`progv`, `java:`, a `return-from` crossing `flet`, redefining a CL function).

When something fails, prefer narrowing to the smallest form that still fails and
running it in the REPL over rereading the code -- the error messages name the
operator and the position.

## Running a program

```bash
rontolisp prog.lisp                                  # interpret
rontolisp prog.lisp -o Prog.class && java Prog       # JVM (class name = file stem, no directories)
rontolisp prog.lisp -o prog.wasm && wasmtime run -W gc prog.wasm
rontolisp prog.lisp -o prog.wasm --component && wasmtime run -W gc=y prog.wasm   # WASI 0.3
rontolisp                                            # REPL
rontolisp -e '(print (+ 1 2))'                       # one form
rontolisp format prog.lisp                           # re-indent in place (whitespace only)
rontolisp test tests/main.lisp                       # run a rove suite; exit 0 passed, 1 failed
```

`rontolisp` is the native binary; `references/getting-started/build.md` says how
to get one, and `java -jar target/rontolisp-*-exec.jar` stands in for it inside a
build tree. The `-o` extension picks the backend. Add `-W exceptions=y` to both wasmtime
commands when the program uses `handler-case` / `ignore-errors` /
`unwind-protect` / `catch` / `throw`, any async form, or a `return-from` or `go`
that crosses a `lambda`; a fetch component also needs `-S http=y`. Other flags
that change the emitted artifact -- `--dynamic`, `--optimize=off`/`=size`, `--no-gc`,
`--simd`, `--blas`, `--no-prune`, `--emit-wit` -- are described under
`references/compiling/`.

## Guides

What Common Lisp knowledge cannot supply: the surfaces rontolisp adds (HTTP,
async, sockets, `java:`, the numeric kernels, WASM contracts, systems and
libraries), and the places where the subset spells its own behavior out in full.
Read the guide before using one of these.

| Topic | Guide |
| --- | --- |
| Vectors & Matrices (linalg) | `references/guides/linear-algebra.md` |
| Vector Kernels & SIMD (vec, linalg) | `references/guides/simd-acceleration.md` |
| Neural Networks (torch) | `references/guides/neural-networks.md` |
| Java Interop | `references/guides/java-interop.md` |
| Asynchronous Programming (async / await) | `references/guides/async.md` |
| HTTP Requests (fetch) | `references/guides/http-fetch.md` |
| Serving HTTP (http-handler) | `references/guides/http-handler.md` |
| TCP Sockets | `references/guides/tcp-sockets.md` |
| Gray Streams | `references/guides/gray-streams.md` |
| Systems (asdf) | `references/guides/asdf-systems.md` |
| Clack Web Applications | `references/guides/clack.md` |
| O/R Mapping (mito, sxql) | `references/guides/mito.md` |
| Testing (rove) | `references/guides/testing.md` |
| Reader Case (Upcasing) | `references/guides/reader-case.md` |
| Math Function Backends | `references/guides/math-backends.md` |
| The Clock and Randomness | `references/guides/clock-and-random.md` |
| WASM Host Boundary (wasm-export / wasm-import) | `references/guides/wasm-host-boundary.md` |
| WIT Contracts (wit-export / wit-import) | `references/guides/wit-contracts.md` |
| wasm-GC Core Module (Default Output) | `references/guides/wasm-gc-module.md` |
| WASI 0.3 Component (--component) | `references/guides/wasm-component.md` |
| WASM Non-GC Output (--no-gc) | `references/guides/wasm-nogc.md` |
| Running WASM in a Browser | `references/guides/wasm-browser.md` |
| Unsupported CL Features | `references/guides/missing-features.md` |
| Compiled eval Limitations | `references/guides/eval-limitations.md` |
| Compiled read/load Limitations | `references/guides/read-load-limitations.md` |

## Unsupported Common Lisp Features

rontolisp is a deliberately small subset of Common Lisp that runs identically on
three backends (interpreter, JVM, WASM). To keep the language compilable to plain
bytecode without a runtime metaobject protocol, many features of full Common Lisp
are intentionally left out.

This page lists **only what is missing or partial**. For what *is* available, see
the [Language Reference](references/reference/special-forms.md), or list it at runtime
with `rontolisp:list-special-forms`, `rontolisp:list-macros`, and
`rontolisp:list-functions`.

| Feature | Status |
| --- | --- |
| restarts | available; no debugger integration (`break`, `*debugger-hook*`) and no condition-restart association |
| `define-symbol-macro` | not available (the lexical `symbol-macrolet` is) |
| `&environment` | accepted in a `defmacro` lambda list but always bound to `nil` (there is no macro-expansion environment object). `&whole` works, in `defmacro` and `destructuring-bind` alike |
| `loop` (extended) | partial (see below) |
| CLOS | partial (static subset + a definition-time MOP subset) |
| `defstruct` `:include` | single inheritance only; slot-overrides `(:include parent (slot default) ...)` work |
| `declare` / `declaim` / `proclaim` / `the` | never change a result; on WASM an array `type` declaration directs the element-accessor emission (smaller, faster modules), everywhere else parsed no-ops |
| `typep` / `subtypep` / `coerce` / `concatenate` | literal (quoted) type specifiers only; `coerce` targets `'list` / `'vector` / `'string` (or a float type), `concatenate` builds those same three sequence families |
| `make-package` / `rename-package` / `delete-package` / `unintern` / `shadow` (runtime) | not available; `export` / `unexport` / `import` / `use-package` ARE, as read/compile-time directives like `in-package`; `defpackage` `:shadow` / `:shadowing-import-from` are errors |
| `eval-when` | treated as `progn` (no phase distinction) |
| `#:name` | reads as a plain symbol, without gensym-style freshness |
| `*modules*` | not available (`require`/`provide` are) |
| complex numbers | not available |
| `catch` / `throw` / `unwind-protect` / conditions under `--no-gc` | compile error (available on every other backend) |

### Multiple values

[`values`](references/reference/functions/values.md) and its consumers are available,
including the values of user functions. The remaining deviations from Common
Lisp:

- a producer that calls `values` in a **non-tail** position and then returns
  normally may leave stale extra values behind, so keep `values` in result
  position (a consumer clears the values it received, so only a `values` call
  that nothing consumes can leave leftovers);
- `funcall #'values` (the first-class value) yields the primary value only in
  compiled programs;
- `multiple-value-call` with a built-in `#'name` keeps the wrapper's fixed
  arity — pass a user function or `lambda` for other argument counts;
- other built-ins with secondary values in CL (`read-from-string`,
  `subtypep`, ...) remain single-value —
  [`find-symbol`](references/reference/functions/find-symbol.md) and
  [`intern`](references/reference/functions/intern.md) do answer the accessibility
  status, and
  [`macroexpand-1`](references/reference/functions/macroexpand-1.md) /
  [`macroexpand`](references/reference/functions/macroexpand.md) do answer
  `expanded-p`.

### Non-local exit

[`catch`](references/reference/special-forms/catch.md) /
[`throw`](references/reference/special-forms/throw.md),
[`block`](references/reference/macros/block.md) /
[`return-from`](references/reference/macros/return-from.md) and
[`tagbody`](references/reference/special-forms/tagbody.md) /
[`go`](references/reference/special-forms/go.md) are available, with two gaps on the
**compiled** backends (the interpreter is unaffected):

- a `return-from` that would cross an `flet`/`labels` local function is not yet
  supported (one crossing a `lambda` is, as a non-local exit);
- `go` must target a tag of a `tagbody` that lexically encloses it; the
  interpreter additionally supports dynamic `go` across function-call
  boundaries, i.e. a tag established by the *caller*. A tag reached from inside
  a nested `lambda` -- the shape a
  [`handler-bind`](references/reference/macros/handler-bind.md) handler that resumes the
  protected loop with a `go` produces, and what quri's `:lenient`
  percent-decoding does -- is lowered like a cross-`lambda` `return-from`: a
  non-local exit that re-enters the `tagbody` at the tag and carries on.

A cross-`lambda` `return-from` or `go`, `catch`/`throw`, `unwind-protect`, and
condition catching all compile in exception-handling mode, so the emitted
wasm-GC modules need `wasmtime -W exceptions=y` (37+); under `--no-gc`
`catch`/`throw`, `unwind-protect` and the condition forms are a compile error.

### Restarts

The condition system is complete through the restart layer:
[`handler-bind`](references/reference/macros/handler-bind.md) handlers run at the signal
point before unwinding, [`restart-case`](references/reference/macros/restart-case.md) /
[`restart-bind`](references/reference/macros/restart-bind.md) /
[`with-simple-restart`](references/reference/macros/with-simple-restart.md) establish
restarts, and [`find-restart`](references/reference/functions/find-restart.md) /
[`invoke-restart`](references/reference/functions/invoke-restart.md) /
[`compute-restarts`](references/reference/functions/compute-restarts.md) /
[`muffle-warning`](references/reference/functions/muffle-warning.md) /
[`abort`](references/reference/functions/abort.md) /
[`continue`](references/reference/functions/continue.md) drive them;
[`cerror`](references/reference/macros/cerror.md) is continuable. What is missing is the
**interactive debugger**: `break` and `*debugger-hook*` do not exist, a restart's
`:report` is stored but never rendered and its `:interactive` function never
runs, and restarts are not associated with conditions (the optional condition
argument of `find-restart`/`compute-restarts` is ignored).
[`check-type`](references/reference/macros/check-type.md) /
[`assert`](references/reference/macros/assert.md) /
[`ccase`](references/reference/macros/ccase.md) still signal without offering a
`store-value` restart. Under `--no-gc` the restart forms degrade to the primary
form (that backend has no condition objects at all); on the wasm-GC backends
only **signaled** conditions are catchable — a runtime trap still aborts.

### The `loop` macro

A bounded subset of the extended [`loop`](references/reference/macros/loop.md) is
available -- that page lists the supported clauses, which include destructuring
patterns, parallel `and`, the anaphoric `it`, `loop-finish` and
`thereis`/`always`/`never`. What is out of scope: `named` (and the
`return-from` it would name); a destructuring pattern does not recognize
lambda-list keywords (`&optional` and friends bind as ordinary variables instead
of signalling); `being` drives hash tables, but its package form
(`being the external-symbols of ...`) parses and iterates the EMPTY sequence,
because there is no runtime intern table.

### Structures and objects

[`defstruct`](references/reference/special-forms/defstruct.md) supports `:include`
inheritance in its single-inheritance form only. Slot-overrides work:
`(:include parent (slot new-default) ...)` re-defaults an inherited slot in the
child's layout while it keeps its inherited index, so the parent's accessors
still read it. An instance prints in the standard `#S(...)` syntax, and
a `#S(...)` literal reads back into an instance -- in source and through the
runtime `read` / `read-from-string` on every backend (a compiled program's
reader has frontend parity; only `#.`, `#+`/`#-` and `#n=`/`#n#` signal there).
A structure that carries a `(:print-object fn)` / `(:print-function fn)` option
prints through that function instead; both options are supported.

CLOS is a **static subset**
([`defclass`](references/reference/special-forms/defclass.md),
[`defgeneric`](references/reference/special-forms/defgeneric.md) /
[`defmethod`](references/reference/special-forms/defmethod.md) dispatching on the first
argument, [`make-instance`](references/reference/macros/make-instance.md) and
[`slot-value`](references/reference/macros/slot-value.md) with literal quoted names).
A slot written with no `:initform` starts UNBOUND, as in CL:
[`slot-boundp`](references/reference/macros/slot-boundp.md) reports it,
[`slot-makunbound`](references/reference/macros/slot-makunbound.md) restores it, and a
read signals `unbound-slot`.
[`change-class`](references/reference/macros/change-class.md) changes an instance's class
in place (the target may be a runtime symbol or a class metaobject), and
`reinitialize-instance` / `shared-initialize` are callable with no user method —
the system defaults fill the supplied initargs, as in CL. A **definition-time MOP
subset** is in:
[`find-class`](references/reference/functions/find-class.md) and
[`class-of`](references/reference/functions/class-of.md) answer real `standard-class`
metaobjects, [`allocate-instance`](references/reference/functions/allocate-instance.md)
works, and a `(:metaclass M)` class option runs the class-definition protocol at
definition time (see [`defclass`](references/reference/special-forms/defclass.md)) — this
is what loads postmodern's DAO layer verbatim. Multiple inheritance works
(class precedence list, slot merge across superclasses). Out of scope: runtime
class construction
(`ensure-class` from computed data, a non-top-level `defclass`, `add-method`,
`compute-applicable-methods`, class redefinition,
`update-instance-for-different-class`) — the class and method sets of a compiled
program are fixed at compile time.

### User-defined packages

[`defpackage`](references/reference/special-forms/defpackage.md) is a literal,
top-level, read/compile-time directive supporting `:use`, `:export`,
`:nicknames` and `:import-from` (`:documentation`/`:size` are accepted and
ignored). `:shadow` and `:shadowing-import-from` are errors (there is no symbol
shadowing). `use-package`, [`export`](references/reference/functions/export.md),
`unexport` and [`import`](references/reference/functions/import.md) exist as the same
kind of read/compile-time directive `in-package` is: a literal top-level call
takes effect for the forms that follow it, on every backend, and a
runtime-computed call works on the interpreter only. Creating or renaming a
package at run time does not: `make-package`, `rename-package` and
`delete-package` are not available.
`unintern` (and the runtime `shadow` / `shadowing-import`) cannot exist here at
all — a symbol IS its name, so there is no intern table to remove it from.
The queries are real: [`find-package`](references/reference/functions/find-package.md),
[`package-name`](references/reference/functions/package-name.md),
[`list-all-packages`](references/reference/functions/list-all-packages.md),
[`package-use-list`](references/reference/functions/package-use-list.md),
[`package-used-by-list`](references/reference/functions/package-used-by-list.md) and
[`package-shadowing-symbols`](references/reference/functions/package-shadowing-symbols.md)
(always `nil`), with the compiled backends answering from a table baked in at
compile time — so a package a compiled program creates later is invisible there.
When several used packages export the same name, the first package in `:use`
order wins instead of signaling a conflict.

### Dynamic (special) variables

Dynamic binding through `let`/`let*` and
[`progv`](references/reference/special-forms/progv.md) is supported, with one
limitation on the **compiled** backends (the interpreter is unaffected): while
normal exit and a `return`/`return-from` that unwinds *across* a special `let`
boundary both restore the binding, an error caught by a handler outside the
`let` (a `go` across it, and on the WASM backends a `return` that also crosses
an `unwind-protect`/`handler-case`) does not. `progv` restores on every exit an
`unwind-protect` covers, including those cases.

### Numeric tower

rontolisp supports integers (including arbitrary-precision bignums), ratios
(`1/3`), and double floats, but **not complex numbers**. A negative square root
yields a float `NaN` rather than a complex result:

```console
> (sqrt -1)
NaN      ; full Common Lisp would return #C(0.0 1.0)
```

### Other omissions

- lambda lists: an extended `defmacro` lambda list (`&whole`, `&optional`,
  `&key`, `&aux`, nested destructuring patterns) routes through
  `destructuring-bind`, which is deliberately lenient -- a missing argument is
  `nil` and a surplus one is ignored rather than signalling; and a function is
  limited to 10 physical parameters on the funcall/apply path.
- user macros are unknown to the runtime `eval` of compiled programs, and a
  `lambda` built at runtime by that `eval` does not parse lambda-list keywords
  (see [Compiled eval Limitations](references/guides/eval-limitations.md)).
- the PRETTY PRINTER produces the text a wide enough line holds, but never
  changes the LAYOUT: no rontolisp stream carries a column, so a logical block
  never wraps, every conditional line break (`pprint-newline` with `:linear` /
  `:fill` / `:miser`, the format directives `~_` / `~:_` / `~@_` / `~i`) is a
  no-op, and `*print-right-margin*` / `*print-miser-width*` / `*print-lines*` are
  accepted and ignored. Only `(pprint-newline :mandatory)` and `~:@_` break a
  line. Every other `*print-*` variable exists and holds the value the printer
  really behaves as -- binding one to a non-default value is what has no effect,
  except for `*print-escape*` / `*print-readably*` / `*print-pretty*` and
  `*print-case*`, which are honored. `*print-case*` converts the case of the
  symbols the printer spells but leaves a symbol nested in a structure, a CLOS
  instance, a hash table or an array of rank other than one at its stored
  spelling ([Reader Case](references/guides/reader-case.md)). The ordinary printing operators do
  not consult `*print-pprint-dispatch*`: an entry fires where the program calls
  the entry function itself.
- `#.` read-time eval is skipped with a warning inside `.asd` files.
- built-in macro names (`cond`, `case`, `when`, `setf`, `push`, ...) cannot be
  redefined; list them with `(rontolisp:list-macros)`.

This list is not exhaustive; rontolisp implements a focused core rather than the
full standard.

## Where everything is

- `references/operators.md` -- every operator by category. The existence check.
- `references/examples.md` -- every worked example in the repository, by
  directory, with the whole program mirrored under `references/examples/`. Before
  writing a program of a shape you have not written here before -- a WASM
  component, an HTTP handler, a Clack app, an `.asd` system, a browser demo --
  open the closest one: it shows the imports, the entry point and the build
  command that no reference page states.
- `references/contents.md` -- every documentation page by title.
- `references/reference/functions/<slug>.md`,
  `references/reference/macros/<slug>.md`,
  `references/reference/special-forms/<slug>.md` -- one page per operator, with a
  runnable example and its own deviations from Common Lisp. `operators.md` links
  each name to its page (the slug is not always the name: `+` is `plus.md`).
- `references/reference/data-types.md` -- arrays, hash tables, the numeric tower,
  the packed float literals `#d(...)` / `#f(...)`.
- `references/reference/packages.md`, `references/reference/function-namespace.md`
  -- the package roster, and what `#'name` may and may not name on a compiled
  backend.
- `references/getting-started/`, `references/compiling/` -- the CLI, the REPL, the
  output shapes and their flags.
