(rontolisp) docs

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 the most notable omissions. For what is available, see the Language Reference, or list it at runtime with rontolisp:list-special-forms, rontolisp:list-macros, and rontolisp:list-functions.

FeatureStatus
defmacro (user macros)available (see defmacro)
&optional / &rest / &key / &auxavailable in defun/lambda (see defun); defmacro takes &rest/&body only
&wholenot available
values / multiple-value-bindavailable, including user-function values (see multiple-value-bind)
block / return-from / tagbody / gonot available
catch / thrownot available
unwind-protectavailable on the interpreter/JVM (see unwind-protect); compile error on WASM
conditions (define-condition, handler-case, ignore-errors, signal)available on the interpreter/JVM (see handler-case); catching is a compile error on WASM. Restarts (handler-bind/restart-case) are not available
flet / labelsavailable (see flet, labels)
macroletnot available
loop (extended)partial (simple-loop subset)
defstructavailable (see defstruct); options/:include are not
CLOSpartial (static subset: defclass, defgeneric, defmethod, make-instance, slot-value)
declare / declaim / proclaim / theavailable as parsed no-ops (see declare)
check-type / assertavailable (lite, no restarts; see check-type)
eval-whenavailable (treated as progn; see eval-when)
typepnot available
coercepartial (literal 'list / 'vector / 'string result types; see coerce)
defpackage (user packages)partial (:use/:export/:nicknames/:import-from; see defpackage)
make-package / export / use-package (runtime)not available
#+ / #- / *features* / #\| ... \|#available (see Data Types)
#. read-time eval / #: fresh uninterned symbolsnot available (#. is a read error, tolerated in .asd files; #:name reads as a plain symbol, accepted as a designator)
require / provideavailable (see require); the *modules* variable is not available
dynamic (special) binding via letavailable (defvar/defparameter/declaim special proclaim a name special; progv is interpreter only)
complex numbersnot available

User-defined macros (defmacro)

User macros are supported — see defmacro for the details, including the backquote template syntax (nested backquote included) and the limitations (&whole/&environment are unsupported, and macros are unknown to the runtime eval of compiled programs). The built-in macro set (cond, case, when, unless, dotimes, dolist, do, setf, push, pop, incf, ...) can be listed with (rontolisp:list-macros); those names cannot be redefined.

Lambda list keywords (&optional, &rest, &key, &aux)

defun and lambda support &optional, &rest, &key, &allow-other-keys, and &aux — see defun for the details. The remaining gaps: &whole is not available, a defmacro lambda list still accepts only required parameters plus one trailing &rest/&body, a function is limited to 7 physical parameters on the funcall/apply path, and a lambda built at runtime by the compiled eval does not parse lambda-list keywords (see Compiled eval Limitations).

Multiple values (values, multiple-value-bind)

Multiple values are available: values, values-list, multiple-value-bind, multiple-value-list, multiple-value-call and nth-value, plus the secondary values of floor/ceiling/round/truncate (remainder), gethash (present-p) and parse-integer (stop position), and the optional divisor argument of the floor family. A (values ...) in result position of a user function reaches the caller's consumer through an internal channel, so the common CL idioms work:

> (defun two () (values 1 2))
> (multiple-value-bind (a b) (two) (list a b))
(1 2)

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;
  • 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, macroexpand-1, intern, ...) remain single-value.

Non-local exit and control flow

Named blocks and arbitrary jumps are not available:

  • block / return-from — there are no named blocks. The only non-local exit is return, which exits the nearest enclosing iteration block established by do / do* / dolist / dotimes.
  • tagbody / go — no label-and-jump control flow.
  • catch / throw — no dynamically scoped exits.
> (block done (return-from done 1) 2)
The function block is undefined

unwind-protect (cleanup on every exit — normal return, error unwind, return/return-from) is available on the interpreter and the JVM backend; the WASM compilers reject it (a WASM error is an uncatchable trap).

Conditions and restarts

The condition-system core is available: condition types are CLOS-subset classes over the built-in hierarchy (condition > serious-condition > error, warning) defined by define-condition (with :report), constructed by make-condition or the typed error/signal designators, and caught by type with handler-case / ignore-errors on the interpreter and the JVM backend (the WASM compilers reject catching: a WASM error is an uncatchable trap).

The restart system is not available: handler-bind, restart-case (accepted as a no-op that keeps only the primary form), restart-bind, invoke-restart, with-simple-restart, cerror, abort, continue and break are absent, and check-type/assert signal without offering a re-store restart.

Local macros (macrolet)

Local functions are available -- see flet and labels. Local macros (macrolet) are not; macros exist only at top level via defmacro.

The loop macro

A bounded subset of the extended loop is available — see loop. It covers numeric/list stepping (for), string stepping (for ... across), the common accumulators (collect, append, sum, count, maximize, minimize, ...), and simple control clauses (while/until, repeat, when/unless, finally, return). Out of scope are destructuring, parallel and between for clauses, being, the anaphoric it, named/loop-finish, and thereis/always/never. The other iteration forms (do, dolist, dotimes, while) remain available.

Structures and objects (defstruct, CLOS)

Structures are available with defstruct, which generates a keyword constructor, a predicate, a copier and setf-able accessors. The defstruct options syntax (:conc-name, :constructor, ...), :include inheritance, and the #S(...) print/read syntax are not supported.

A static CLOS subset is available: defclass (single inheritance, :initarg/:initform/:reader/:accessor slot options), make-instance and slot-value (both with literal quoted names), and defgeneric / defmethod dispatching on the first argument (eql, class, and built-in-type specializers), including standard method combination — :before/:after/:around qualifiers, call-next-method, and next-method-p (for class and default methods). Out of scope: multiple inheritance, specializers on later arguments, slot-boundp, and the MOP / runtime class operations (find-class, change-class, add-method, class redefinition) — the class and method sets of a compiled program are fixed at compile time.

Type declarations, typep, and coerce

Type declarations are accepted as parsed no-ops: declare, declaim, proclaim, and the all parse and have no effect, so annotated sources load unchanged. check-type and assert provide actual runtime checks (lite, without restarts). The runtime helper typep is not available. coerce is available for the literal result types 'list, 'vector and 'string (the result type must be a quoted literal, like map's); other result types are not supported.

User-defined packages

New packages can be defined with defpackage, as a literal, top-level, read/compile-time directive supporting the :use, :export, :nicknames and :import-from clauses (:documentation/:size are accepted and ignored; see Packages). :shadow and :shadowing-import-from are errors (there is no symbol shadowing), and there is no runtime package manipulation: make-package, export, import, use-package, find-package, and rename-package are not available. A package's set of exported (external) symbols is fixed when it is defined; the single/double colon qualifiers (pkg:name for external symbols, pkg::name for internal ones) work as in Common Lisp (see Packages). The standard nicknames common-lisp/common-lisp-user resolve to cl/cl-user, and #:name designators are accepted. When several used packages export the same name, the first package in :use order wins instead of signaling a conflict.

Dynamic (special) variable binding

Dynamic (special) variable binding is supported. defvar, defparameter, and defconstant proclaim their variable special (as does (declaim (special *x*))), and a let/let* of a special name establishes a dynamic binding — visible to functions called within the extent and restored on exit — rather than a lexical one:

> (defvar *factor* 1)
> (defun scale (n) (* n *factor*))
> (let ((*factor* 10)) (scale 5))
50

The bindings are per thread of control, so concurrent rontolisp:http-handler requests never see each other's. Two limitations remain on the compiled backends (the interpreter is unaffected): progv (runtime-computed lists of symbols) is interpreter-only and a compile error on the JVM/WASM backends, and a return/return-from that unwinds across a special let boundary does not restore the global there (normal exit and error abort are fine).

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:

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

Other omissions

symbol-macrolet is not available, and progv is interpreter-only (a compile error on the JVM/WASM backends); eval-when is available, treated as progn. This list is not exhaustive; rontolisp implements a focused core rather than the full standard.