Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Terms

Programs and libraries

A program is a file of import forms followed by definitions and expressions, which run in order. A library is an R7RS define-library with import, export and begin sections, stored at a path that matches its name: (sample geometry) lives in sample/geometry.sld under a search directory. Import sets may use only, except, prefix and rename.

(import
 (snail-core base)
 (snail-core io))

(: fibonacci (-> I64 I64))
(define (fibonacci n)
  (if (< n 2) n (+ (fibonacci (- n 1)) (fibonacci (- n 2)))))

(display (fibonacci 30))
(newline)

src/ is always on the search path. ./scripts/snail-tool run -I DIR program.sld adds more.

A library file may instead hold a generate-library recipe: a program whose value is the library’s define-library form, as a Syntax from (snail-core syntax). The compiler builds and runs it first:

(generate-library (sample table)
  (import
   (snail-core base)
   (snail-core syntax))
  (library-syntax (list "sample" "table")
                  (list "squares")
                  (list (list "snail-core" "base"))
                  (list (form "define" (list (SymbolSyntax "squares")
                                             (form "list" (map (lambda (i) (IntegerSyntax (* i i))) (iota 6))))))))

The program writes the syntax as bytes that the compiler decodes, so no text is printed or parsed in between, and a BytesSyntax reaches the compiler as the bytes it holds. datum->syntax from (snail-core datum) turns a Scheme datum into syntax. Every generated form is located at the recipe.

Forms

FormMeaning
(define name value), (define (name param ...) body ...)Definition.
(lambda (param ...) body ...)Function with a fixed number of parameters.
(if test then [else])test is Bool; without else the result is Unit.
(begin form ...)A body: definitions and expressions in order.
(let ((name value) ...) body ...), (let loop ((name value) ...) body ...)Local bindings and loops.
(match value (pattern body ...) ...)Pattern matching, checked for exhaustiveness and unreachable clauses.
(: name type)Signature for the following definition.
(ann expression type)Static type annotation.
(type-convert expression type)Converts to or from Any; a downcast is checked at run time.
(type-test expression type)Whether an Any holds a value of type, without trapping.
(struct (Name param ...) ((field type) ...))Record type.
(enum (Name param ...) (Case [((field type) ...)]) ...)Tagged sum.
(type (Name param ...) type)Transparent alias.
(typeclass (Class param ...) (: method type) ...)Class of types with methods.
(instance (Class type ...) [(requires constraint ...)] (define ...) ...)Instance.
(effect Name ((operation type) ...) [(host "module" ((operation "symbol") ...))])Effect with operations and optional host defaults.
(handle Name ((operation implementation) ...) body ...)Handles an effect for the extent of body.
(extern name "module" "symbol" type)Wasm import, asserted total.
(expose name "symbol")Exports a monomorphic top-level function to the host; its arguments and result must be unrestricted.
(define-syntax name (syntax-rules (literal ...) (pattern template) ...))Hygienic macro.

(snail-core base) adds let*, letrec, cond, case, when, unless, and, or, do, list, string and a variadic string-append as macros.

Bodies and scope

Library and program top levels see all of their definitions, so top-level functions may be mutually recursive. A local body is sequential: a definition is visible to the forms after it, and a local function is also visible to itself. Write mutually recursive helpers at the top level.

Functions have fixed arity. Where Scheme uses variadic procedures, Snail-Core uses macros (list, string-append) or lists.

Toolchain terms

These names distinguish source organization, compilation and execution. Project descriptions, builds and the native snail dev editor are implemented. The application process and persistence workflow remains proposed.

TermMeaning
ProjectOne directory of source files and its project description. A plain directory can be edited before a description is added.
WorkspaceA collection of projects. The tools support one project; they do not introduce a workspace object.
Build targetA named description of what to build. It produces one output directory, which may contain multiple files.
Artifact bundleThe complete output of a browser build target, resolved by name during compilation.
Run targetA named command for running a program build with its arguments.
Build attemptOne invocation of project building, with an ID, diagnostics and a success/failure result.
Compiler sessionCached expansion, checking and emitted code retained between builds.
Development sessionA running snail dev: file watching, persistent compilation and the editor’s services. Application control remains planned.
Application processAn OS process running a program. A browser frontend is a client of a server process, not a run target.
RuntimeThe scheduler and effect bindings executing a program’s tasks. It is distinct from both the compiler session and the OS process.
HostThe platform environment, such as Node or a browser, providing APIs to the runtime.
WorkerA separate execution agent used for CPU work. Here a native compiler worker keeps its own compiler state; it is not a browser Service Worker or a Snail-Core task.
Working stateIn-memory values used by the running application, including changes not yet saved.
Persisted dataThe application’s chosen representation committed to storage independently of the running code. It need not encode every transient resource or cache.

Incremental compilation reuses compiler work. Restart starts a fresh application process; the application can reconstruct working state from persisted data. Hot reload (also called live reload) replaces running code while preserving application data under a stated contract. Today’s browser uses state handover; the proposal uses durable typed tables and disposable cached views, recomputed on demand after ordinary startup. Recompiling incrementally does not by itself preserve application state, and moving execution into a worker does not supply a preservation contract.