Scheme in the Browser with Guile Hoot and WebAssembly

Robin Templeton (they/them) <robin@spritely.institute>

https://spritely.institute/

FOSDEM 2024

What is Spritely?

We are building decentralized, secure infrastructure and standards for networked communities!

We are a 501(c)(3) non-profit research institution

Research means collaboration

Goblins

Distributed, transactional programming with a security model you can understand.

"If you don't have it, you can't use it!"

More later in Christine's talk!

Getting Goblins into the hands of users

  • Everyone has a browser, so Goblins should run in the browser
  • …but Goblins is written in Guile Scheme, not JavaScript
  • We need WebAssembly

What is WebAssembly?

  • W3C open standard
  • Low-level compilation target for the web
  • Available in browsers since 2017
  • Wasm GC supported in Firefox and Chromium since late 2023

Why should I give a hoot?

  • JavaScript is no longer the only game in town
  • Fewer compromises than compiling to JavaScript
  • Capability security model
  • Production runtimes are fast
  • Growing number of non-browser runtimes

What is Guile?

  • Flexible Scheme system
  • Supports several Scheme standards, libraries, and extensions
  • Good bytecode VM and JIT
  • Also a platform for hosting non-Scheme languages
  • Used in Unix-like environments mostly… until now

Decisions, decisions

  • Option 1: Compile Guile VM via emscripten
    • Ship faster
    • Run slower (no JIT for Guile bytecode)
    • Poor integration with JS
  • Option 2: Compile directly to Wasm GC
    • Ship slower
    • Run faster (no Guile bytecode)
    • Good integration with JS
  • We chose option 2

Introducing: Guile Hoot

  • Scheme to Wasm compiler
  • Built for Wasm GC
  • Includes a full Wasm toolchain

"Bring your whole self." — Andy Wingo

Goals

  1. Run Goblins applications in the browser
  2. Advocate for all dynamic languages in the Wasm ecosystem
  3. Provide an alternative to the mainstream Wasm toolchain

Example

This is the update loop for a particular cellular automaton:

(define (update from to)
  (do ((y 0 (+ y 1)))
      ((= y grid-size))
    (do ((x 0 (+ x 1)))
        ((= x grid-size))
      (let* ((t (grid-ref from x y))
             (t* (cond
                  ((= t empty) empty)
                  ((= t cu)
                   (if (<= 1 (neighbors from x y) 2) ehead cu))
                  ((= t ehead) etail)
                  ((= t etail) cu))))
        (grid-set! to x y t*)))))

Can we run it in a web browser?

Yes!

Welcome to Wireworld!

Status

  • Most of R7RS-small Scheme
  • Some Guile and R6RS features
  • JS interoperability library
  • New in 0.3.0, released this week: R6RS library system, hash tables, debug names, R7RS benchmarks

Hoot is a compiler

Hoot provides a new compiler backend and Wasm runtime for Guile

Scheme →
Tree-IL →
CPS →
Guile Bytecode Wasm

libguile
guile-hoot

What Hoot needs from Wasm

  • GC
  • Tail calls
  • Strings

Latest Firefox and Chromium have it all!

Should be in stable releases of all major browsers soon

Pros and cons

The good:

  • WAT is nice, especially the folded form
  • We have tail calls!
  • Reference type system is pretty darn good

The bad:

  • No stringref in production Wasm hosts
  • No first-class continuations
  • Limited GC features

Example: Inline Wasm

  • WebAssembly Text format uses S-expressions…
  • …so why not embed Wasm directly into Scheme?
  • Composes well with the Scheme code around it
(define (port? x)
  (%inline-wasm '(func (param $obj (ref eq))
                       (result (ref eq))
                       (if (ref eq)
                           (ref.test $port (local.get $obj))
                           (then (ref.i31 (i32.const 17)))
                           (else (ref.i31 (i32.const 1)))))
                x))

Hoot is a library of Wasm tools

  • Hoot provides its own self-contained Wasm toolchain
  • Write, compile, and execute Wasm in one place
  • Alternative to emscripten, binaryen, wabt, etc.

The toolchain

  • Available as a set of Scheme modules for easy programmatic access
  • Extensions to Guile's guild tool for hooking into build systems
  • Contains:
    • WAT parser
    • Assembler
    • Disassembler
    • Linker
    • Dumper
    • Interpreter

Example: Testing Scheme compiled to Wasm

scheme@(guile-user)> ,use (hoot reflect) (wasm parse)
scheme@(guile-user)> (define hoot-square
                       (compile-value '(lambda (x) (* x x))))
scheme@(guile-user)> hoot-square
$1 = #<hoot #<procedure>>
scheme@(guile-user)> (hoot-square 4)
$2 = 16

The future of Hoot

  • More R7RS-small features
  • More Guile features
  • Fibers!
  • Goblins!

The future of Wasm

  • Hoot has had an outsized impact on the Wasm GC proposal
  • Come all ye dynamic language implementers!
  • Get involved with the Wasm CG
  • We changed things and you can, too!

Conclusion

  • Scheme is now an alternative to JavaScript
  • Wasm should have space for all languages
  • Hoot is for Scheme but not only Scheme
  • Hoot provides an alternative Wasm toolchain

Hoot REPL

Strigoform

Play Strigoform!

We are a research institution! Work with us!

Questions?

Learn more about Hoot:

https://spritely.institute/hoot/

Join our community forum!

https://community.spritely.institute/

Chat with us!

#spritely on irc.libera.chat