Skip to content

Standards Conformance

Kaappi's core language is a complete implementation of R7RS-small. On top of that, a small set of runtime subsystems — fibers, an I/O reactor, cross-thread channels — deliberately extend beyond what R7RS-small specifies, since the standard has no concurrency model at all. This page states both plainly: where the core language has known gaps against the spec, and where Kaappi goes beyond the spec's scope on purpose.

Other Schemes document this distinction the same way — Guile's "R7RS Incompatibilities", Gauche's "Standard conformance" chapter, CHICKEN's "Deviations from the standard" — and this page follows the same shape.

Core language

Every identifier in R7RS Appendix A is implemented: 601 built-in procedures, 32 syntax forms, and all 14 standard libraries. The R7RS conformance test suite passes in full. See CONFORMANCE.md in the core repo for exact counts and per-SRFI coverage detail — this page doesn't restate numbers that live there, so it can't drift out of sync with them.

Known gaps against R7RS-small

Gap Detail
syntax-case Not implemented — R7RS-small itself deliberately standardizes only syntax-rules; syntax-case is R6RS/implementation-specific territory (Chez, Racket), not a Kaappi omission against the spec it targets.
call/cc across REPL top-level forms A continuation captured in one top-level REPL expression can't re-enter a later one. Shared behavior with Guile, Chibi, Chicken, Chez, and Racket — not a Kaappi-specific limitation.
Fiber parking inside native higher-order procedures Callbacks driven by map, for-each, vector-map, dynamic-wind, and force can park a fiber (e.g. on an empty channel) and resume later. Other higher-order procedures — SRFI-1's fold/filter/find, sort, hash-table-walk, eval, and others — are native drivers whose call state can't be suspended; a fiber that blocks inside one of those needs restructuring into a plain Scheme loop. See Concurrency.
SRFI coverage 85 SRFIs supported; a handful of optional or non-mutating-only procedures aren't implemented (linear-update list variants, string-xcopy!, and similar). See SRFI Support and CONFORMANCE.md for the exact list.

Extensions beyond R7RS-small's scope

R7RS-small has no concurrency model — no threads, no I/O multiplexing, no shared memory. Everything below is deliberate, documented extension into that unspecified territory, tracked as Kaappi Enhancement Proposals (KEPs).

Subsystem KEP Status Detect from code
Fibers + I/O reactor KEP-0001 Final — shipped (cond-expand (kaappi-fibers ...)), (cond-expand (kaappi-reactor ...))
SRFI-18 OS threads — (R7RS-small doesn't cover threads; SRFI-18 predates this project) Shipped (cond-expand (kaappi-threads ...)), or (cond-expand ((library (srfi 18)) ...))
Cross-thread channels KEP-0002 Accepted — core mechanism shipped; the two wakeup-path correctness issues open at 0.14.x (#1487, #1489) were fixed in v0.15.0 Not yet exposed via cond-expand: KEP-0004 gates a kaappi-shared-channels identifier on the wakeup path shipping with its review findings resolved — the fixes have shipped, the identifier has not yet; check kaappi features on your build
Shared flat numeric buffers KEP-0003 Draft — unimplemented, gated behind KEP-0002 usage data None — doesn't exist yet

See Concurrency for how to use fibers, the reactor, and threads today.

Detecting subsystems from code

cond-expand (SRFI 0) is the portable way to branch on what a given Kaappi build actually has, rather than probing behavior at runtime:

(import (scheme base) (kaappi fibers))

(cond-expand
  (kaappi-reactor
    ;; a multiplexed async I/O reactor is compiled in (true on every
    ;; target today, including wasm32-wasi)
    (define (serve conn) (spawn (lambda () (handle conn)))))
  (else
    ;; fall back to a single-connection blocking loop
    (define (serve conn) (handle conn))))

A library that wants to degrade gracefully on a build where a subsystem is absent can check for the library itself instead of (or in addition to) the bare feature identifier:

(cond-expand
  ((library (kaappi fibers)) (import (kaappi fibers)))
  (else (define (spawn thunk) (thunk))))  ; synchronous fallback

SRFI availability has the same two spellings: ((library (srfi <n>)) ...) probes the import, and the shorthand srfi-<n> identifiers — e.g. (cond-expand (srfi-1 ...)) — answer through the same check, so both always agree with what (import (srfi <n>)) would do, including under --sandbox and on WASM builds. They work in expression- and define-library-level cond-expand alike. Like (library ...) requirements, srfi-<n> is a derived probe resolved on demand rather than a bare feature identifier, so it does not appear in (features).

Identifier True when
kaappi-fibers (kaappi fibers) is compiled in — every target, including wasm32-wasi
kaappi-reactor An OS-level I/O multiplexing reactor (kqueue/epoll/poll_oneoff) is compiled in
kaappi-threads SRFI-18 OS threads are compiled in — every target except wasm32-wasi
kaappi-diagnostics (kaappi diagnostics) (stable KP error codes on error objects) is compiled in — every target, including wasm32-wasi
posix The build targets a POSIX-flavored platform — every target except Windows, including wasm32-wasi
windows The build targets Windows — present in place of posix, so portable code can branch with (cond-expand (windows ...) (else ...))
srfi-<n> SRFI n is importable on this build — the derived probe described above, equivalent to ((library (srfi <n>)) ...)

--sandbox mode and a WASI host's actual (as opposed to compiled-in) I/O capability are runtime facts that cond-expand — an expand-time construct — cannot express; see KEP-0004 for the open question on whether a companion runtime predicate is worth adding.

Discovering capabilities from the command line

cond-expand answers these questions inside a program. kaappi features answers them at the command line — for a person, or an agent, sizing up a build before running anything:

$ kaappi features --json
{"version":"0.15.0", … ,"features":["r7rs","kaappi", … ,"kaappi-fibers",
 "kaappi-reactor","kaappi-diagnostics","kaappi-threads"],
 "srfis":{"builtin":[1,9,13, … ],"portable":[0,2,4, … ]},
 "limits":{"initial_frame_capacity":480, … }}

It reports the version and git build id, target triple, build mode, the same compiled-in subsystem identifiers as the table above (whether --sandbox is available among them), and the built-in vs portable SRFIs — all derived from the one table that drives cond-expand, so the CLI and the language can never disagree about what a build has. Run kaappi features with no --json for a human-readable table. See the kaappi features reference for the full JSON shape.

Further reading