R⁷RS Scheme for WebAssembly

Get Started Design


Full (planned) support for R⁷RS.


Prioritize WebAssembly as a compilation target and FFI.


"Infantile" would be a generous characterization.

WebScheme compiles Scheme code into WebAssembly modules. This allows run-time or compile-time linkage to other WebAssembly modules, including those implemented in foreign languages via components.

WebScheme programs containing library definitions are able to produce Wasm modules. For example, this Scheme library may result in this Wasm module, pending the adoption of the GC and type imports proposals:


Wasm (as well as R⁷RS) is evolving fast. Most of the advanced features on which WebScheme intends to depend (GC, interface types, type imports, etc.) are in various stages of proposal, but based on the proposals so far, Wasm should eventually enable a compact, performant implementation.

;; Library names are resolved to compiled Wasm module files by
;; converting the library name parts to their external representation
;; and concetenating them as subdirectories of the root library directory.
;; For example, `(com |Foo 😀| 12 |\x15|)`
;; would resolve to `${ROOT}/com/Foo\ 😀/12/\x15.wasm`,
;; containing either a Wasm core module also compiled by WebScheme,
;; or an adapter module compiled by anything.
(define-library (library name)

  ;; The compiler can either produce a standalone executable
  ;; or keep this import dynamic.
  (import (scheme base))

  ;; Globals and functions can be directly imported.
  (import (only (com |Foo 😀| 12 |\x15|) external-function))

    ;; Macros are exported as simple global `syntax-rules` objects
    ;; and also listed in a special table.
    (define-syntax macro
      (syntax-rules ()
        ((macro x ...) (list x ...))))

    ;; Functions are exported as Wasm functions.
    (define (function x y)
      (+ (external-function x x) y))

    ;; All other objects are exported as globals.
    (define value "hello")

    ;; All expressions are evaluated in the module's start function.
    (display "Instantiated!"))

  (export macro function value))
;; The compiled core module.
;; Using extensions, it would also be possible to produce an adapter module.

  ;; The special "s-lang" module is the runtime.
  ;; Types and functions are imported as needed and always statically linked.
  ;; Two S-lang programs compiled against different versions of the runtime
  ;; may have incompatible ABIs.
  (import "s-lang" "t-obj" (type $t-obj))                    ;; Super-type for all objects.
  (import "s-lang" "t-pair" (type $t-pair))                  ;; Pair type.
  (import "s-lang" "t-string" (type $t-string))              ;; String type.
  (import "s-lang" "t-syntax-rules" (type $t-syntax-rules))  ;; `syntax-rules` (macro) type.
  (import "s-lang" "t-null" (type $t-null))                  ;; Null type.
  (import "s-lang" "null" (global $null))                    ;; Null value.

  ;; The import compiler can statically analyze libraries
  ;; to only import what it needs for import sets without an `only` clause.
  ;; `display` only takes 1 parameter: the list of parameters.
  ;; This is true of all functions defined as `(<name> . <parameters>)`,
  ;; and allows a rare (if limited) opportunity for static typing.
  (import "(scheme base)" "display" (func $display (param $t-pair)))

  ;; The module name is the canonical external representation
  ;; of the entire library name.
  ;; The function signature is determined by inspection of the dependency
  ;; at compile-time.
  (import "(com |Foo 😀| 12 |\x15|)" "external-function" (func (param $t-obj) (result $t-obj)))

  ;; `syntax-rules` (a.k.a macros) can be imported and exported,
  ;; and have a run-time representation.
  ;; Although dynamic imports will not affect already-running code
  ;; (and at that point could only really be used with `eval`),
  ;; static (as configurable by compiler flag) imports apply to subsequent code.
  (global $macro (export "macro") $t-syntax-rules ...)

  (func $function (export "function") (param $x $t-obj) (param $y $t-obj) ...)

  (global $value (export "value") $t-string ...)

  (func $_start ...)

  (start $_start))