S-exp GPU
All pages
Docs · The languageMarkdown

It is Common Lisp

The authoring language is Common Lisp, evaluated at compile time. It is a subset, not an ANSI Common Lisp implementation: if you know Common Lisp, assume it behaves that way except for the differences below.

The Lisp runs while the file compiles. Closures, macros, list processing and variable mutation construct models, optimizers and the experiment; the compiler then checks the resulting training contract. Authoring code does not have to be tensor code. No Lisp runs during training. See how it works.

What is Common Lisp here

Lexical closures, parallel let and sequential let*, setq, flet, labels, macrolet, full lambda lists (&optional, &rest, &key, &allow-other-keys, &aux, supplied-p variables), destructuring macro lambda lists (&whole, &body, &environment, nested patterns), destructuring-bind, multiple values, backquote with , and ,@, unhygienic macros with intentional capture, fresh gensyms, intern, macroexpand and macroexpand-1, dotimes and dolist, apply, incf, decf, push, and numeric comparisons (/= requires pairwise distinct arguments).

The reader

syntaxreads as
42, -3an i64 integer
0.5, 1e-8an f64 float; write 0.5, never .5
"text"a string
name, model.embeda symbol; case sensitive, may contain -+*/<>=!?.&_%; kebab-case by convention
:namea keyword
true, false, nilliterals; nil is also the empty list
(a b c)a list
[a b c]a vector whose elements evaluate: shapes, axes, lists of models
'x `x ,x ,@xquote, backquote, unquote, splice
; ...a comment to the end of the line

Files use the .sx extension and one layout; see fmt.

The differences

  • One namespace. Functions and variables share it. Call a bound function or model as (f x); there is no funcall, no function and no #'.
  • Case sensitive names.
  • Truth. nil and false are false; everything else is true, including zero. See truth.
  • Immutable collections. Lists are proper lists; there are no dotted pairs and no mutable cons cells. setq rebinds a variable; it never mutates a list, a vector or a tensor.
  • defvar is an ordinary module binding, not a special variable. Defining a name twice in one module fails.
  • Modules, not packages. Files import with require. intern takes an optional macro environment instead of a package.
  • Numbers. Integers are i64, floats f64, and / always returns a float. No bignums, no ratios. Overflow and division by zero are E-EVAL-007.
  • Not implemented: block, return-from, symbol-macrolet, generalized setf (incf, decf and push take variable names only), reader macros, conditions and restarts, CLOS.
  • Static shapes. Loops unroll into graphs; a condition on a tensor needs where. See tensor operations.
  • Optimizer limits. Each optimizer update graph owns one parameter, and optimizer states start at zero. See writing optimizers.

Evaluation limits

limitdefaultcode
compile-time work: evaluation, calls, macro expansion, quasiquote traversal, range construction10,000,000 units per compilation; raise with SEXPGPU_EVAL_LIMITE-EVAL-041
recursion depth of user functions; there is no tail-call optimization256 framesE-EVAL-040

Macro expansion chains are iterative and governed by the work budget only. The limit applies to compilation and never changes the training loop.

The pages

A worked example combines these in one complete experiment.