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.
| Feature | Status |
|---|---|
defmacro (user macros) | available (see defmacro) |
&optional / &rest / &key / &aux | available in defun/lambda (see defun); defmacro takes &rest/&body only |
&whole | not available |
values / multiple-value-bind | available, including user-function values (see multiple-value-bind) |
block / return-from / tagbody / go | not available |
catch / throw | not available |
unwind-protect | available 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 / labels | available (see flet, labels) |
macrolet | not available |
loop (extended) | partial (simple-loop subset) |
defstruct | available (see defstruct); options/:include are not |
| CLOS | partial (static subset: defclass, defgeneric, defmethod, make-instance, slot-value) |
declare / declaim / proclaim / the | available as parsed no-ops (see declare) |
check-type / assert | available (lite, no restarts; see check-type) |
eval-when | available (treated as progn; see eval-when) |
typep | not available |
coerce | partial (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 symbols | not available (#. is a read error, tolerated in .asd files; #:name reads as a plain symbol, accepted as a designator) |
require / provide | available (see require); the *modules* variable is not available |
dynamic (special) binding via let | available (defvar/defparameter/declaim special proclaim a name special; progv is interpreter only) |
| complex numbers | not 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
valuesin a non-tail position and then returns normally may leave stale extra values behind, so keepvaluesin result position; funcall #'values(the first-class value) yields the primary value only in compiled programs;multiple-value-callwith a built-in#'namekeeps the wrapper's fixed arity — pass a user function orlambdafor 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 isreturn, which exits the nearest enclosing iteration block established bydo/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.