Scheme: Notes and Examples

Table of Contents

Scheme

Getting started

brew install chezscheme guile

Pairs

  (define (sh/wal/pair f r)
    (lambda (op)
      (op f r)))

  (define (sh/wal/first p)
    (p (lambda (f r)
         f)))

  (define (sh/wal/rest p)
    (p (lambda (f r)
         r)))

  (sh/wal/first
   (sh/wal/rest
    (sh/wal/pair
     1
     (sh/wal/pair
      2
      (sh/wal/pair 3 0)))))
const pair = (f, r) => (op) => op(f, r);
const first = (p) => p((f, r) => f);
const rest = (p) => p((f, r) => r);

console.log(first(pair(1, 2)));

Lists

  (define (sh/wal/accumulate op initial sequence)
    "Reduce a list to another structure"
    (if (null? sequence)
     initial
     (op (car sequence)
         (sh/wal/accumulate op initial (cdr sequence)))))

  (sh/wal/accumulate + 0 (list 1 2 3 4 5))

Map Reduce

(define (sh/wal/square n)
(* n n))

;; (define (filter predicate sequence))

(map sh/wal/square (list 1 2 3 4 5))

Numbers

  (define (sh/wal/pct numer denom)
    (exact->inexact (* 100 (/ numer denom))))

  (sh/wal/pct 1111 3333)

Functions

  (define sh/wal/fib
    (lambda (n)
      (if
       (or (= 0 n)
           (= 1 n))
       n
       (+ sh/wal/fib(- 1 n) fib(- 2 n)))))

  (sh/wal/fib 2)

Conditionals

  (define (sh/wal/even? n)
    (cond
     ((< n 2) (= 0 n))
     (else
      (sh/wal/even? (- n 2)))))

  (sh/wal/even? 1000)

Recursion

  (define sh/wal/fact
    (lambda (n)
      (if
       (= 0 n)
       1
       (* n (sh/wal/fact (- n 1))))))

  (sh/wal/fact 10)

Math

(define (sh/wal/limit n)
    (* 2 (floor (sqrt n))))

(sh/wal/limit 1000)
  (define (sh/wal/gcd a b)
    (cond
     ((= a b) a)
     ((> a b) (sh/wal/gcd (- a b) b))
     ((< a b) (sh/wal/gcd a (- b a)))))

  (sh/wal/gcd 120 35)

Closures

(define a 5.3)
(define b 4.7)
(define c 2.8)

(define (sh/wal/area a b c)
  (let ((s (/ (+ a b c) 2)))
    (sqrt (* s (- s a) (- s b) (- s c)))))

(sh/wal/area a b c)

Modules

Abstractions

2026 Review

A retrospective on where Scheme implementations stand in early 2026, with particular attention to R7RS standardization progress, tooling maturity, and emerging use in agentic/tool-use contexts.

Guile 3.0.10+ (2025–2026)

GNU Guile 3.0.x continued to ship incremental releases through 2025 into 2026. The headline win for this series is the JIT compiler landed in 3.0.0 (2020) finally reaching production stability: tail-call elimination across the JIT boundary, improved inlining heuristics, and better profiling support. Key milestones in the 3.0.9–3.0.10+ window:

  • Baseline JIT now active by default on x86-64 and aarch64.
  • guild tool improvements: better dependency tracking for compiled .go artifacts.
  • Cleaner POSIX interface coverage in (ice-9 popen) and (ice-9 ftw).
  • Ongoing GC tuning; the Boehm GC dependency is a persistent pain point for embedding scenarios.

R7RS-small compliance remains high: Guile passes the large majority of the R7RS test suite. The outstanding gap areas are tail-position semantics in define-record-type and some edge cases in parameterize / dynamic-wind interaction.

Chez Scheme and the Racket CS Backend

Chez Scheme (now developed openly at github.com/cisco/ChezScheme) continues to set the performance bar for Scheme implementations. Notable 2025–2026 developments:

  • Racket's CS backend (built on Chez) is now the default for racket distributions; the old expander/VM is no longer the recommended path.
  • Chez added initial support for R7RS library syntax, closing a long- standing compatibility gap with community SRFIs.
  • Cross-compilation targets expanded: riscv64 support landed; ARM Cortex-M work-in-progress.

The Chez/Racket-CS combination gives the ecosystem an unusually capable implementation that combines ahead-of-time native compilation with a mature macro expander and module system.

R7RS-large Progress and SRFI Adoption

R7RS-large (the "Red Edition" successor to R7RS-small) defines a standard library via a curated set of SRFIs. As of 2026:

SRFI Topic Adoption
1 List library Universal (all major implementations)
9 Record types Universal
13 String library Guile, Chibi, Chicken, Gambit
14 Character sets Guile, Chibi, Chicken
45 Lazy evaluation Guile, Chibi, Chicken, Gambit, Chez
69 Hash tables Guile (native), Chibi, Chicken
106 Basic socket interface Chicken, Gambit; Guile (partial)
133 Vector library Most R7RS-small implementations
158 Generators and accumulators Chibi, Chicken, Gambit
193 Command line Chibi; Guile (partial)

The R7RS-large editors' group continues to publish ballot documents. The "Red Edition" scope is largely stable; the focus has shifted to implementation uptake rather than further specification changes.

Scheme in the Agent Era

Scheme has seen modest but real uptake in the emerging agentic/tool-use space, driven primarily by Guile's role as the extension language for GNU projects and Racket's strong macro/DSL story.

  • Guile as an MCP substrate: Several experimental projects embed Guile as a sandboxed evaluation layer in Model Context Protocol (MCP) servers. The appeal is Scheme's homoiconicity — tool definitions are data, and tool results can be manipulated as first-class values.
  • Racket for DSL agents: Racket's macro system makes it natural to define domain-specific tool languages; at least two published agentic frameworks (one from a university group, one from a startup) expose Racket-based tool-definition languages.
  • Chicken Scheme on edge: Chicken's native-code compiler and small runtime footprint make it attractive for running lightweight inference scaffolding on constrained devices.
  • Babashka influence: While Babashka is Clojure-based, its success in scripting has renewed interest in Guile and Gambit as portable Scheme scripting runtimes — fast startup, small binary, rich POSIX access.

No major MCP SDK for Scheme exists as of April 2026; the closest is an unofficial Guile binding to the MCP JSON-RPC protocol, maintained by the community at codeberg.org/guile-mcp.

Guile on FreeBSD

FreeBSD (the platform this site runs on) ships Guile in the ports tree under lang/guile3. The story is workable but requires attention:

# Install Guile 3 on FreeBSD via pkg
pkg install guile3

# Or from ports (gives configure control)
# cd /usr/ports/lang/guile3 && make install clean

# Verify installation
guile3 --version

# Run a quick smoke test
guile3 -c "(display (+ 1 2)) (newline)"

Known friction points on FreeBSD as of 2026:

  • The port links against Boehm-GC from devel/boehm-gc. On FreeBSD 15.0-RELEASE this works cleanly, but pinning the port version matters when doing binary packages across major releases.
  • JIT support on FreeBSD/amd64 is present but requires W^X-compliant memory allocation; the Guile port patches this correctly.
  • guild compile can be slow on first build due to the bootstrapping pass; subsequent incremental compiles are fast.
  • The BEADS_NO_DAEMON=1 pattern (used in this repo's worktrees) pairs well with Guile scripting since both avoid long-lived daemon processes.

Recommended approach for Guile-based tooling on this FreeBSD host:

# Add to ~/.bashrc or ~/.zshrc
export GUILE_LOAD_PATH="${HOME}/.local/share/guile/site/3.0:${GUILE_LOAD_PATH}"
export GUILE_COMPILED_PATH="${HOME}/.cache/guile/ccache/3.0-LE-8-4.4:${GUILE_COMPILED_PATH}"

# Confirm Guile sees local site directory
guile3 -c "(for-each (lambda (p) (display p) (newline)) %load-path)"

R7RS Implementation Landscape

digraph scheme_landscape {
  rankdir=LR;
  node [fontname="Helvetica", shape=box, style=filled];

  // Standards
  r5rs  [label="R5RS (1998)", fillcolor="#e8f4e8", shape=ellipse];
  r7rs_small [label="R7RS-small (2013)", fillcolor="#b8deb8", shape=ellipse];
  r7rs_large [label="R7RS-large\n(Red Edition, in progress)", fillcolor="#88c088", shape=ellipse];

  // R7RS-small implementations
  guile   [label="Guile 3.0.10+\n(GNU/FreeBSD)", fillcolor="#cce0ff"];
  chez    [label="Chez Scheme\n(+ Racket CS)", fillcolor="#cce0ff"];
  chibi   [label="Chibi-Scheme\n(embeddable)", fillcolor="#cce0ff"];
  chicken [label="Chicken 5\n(native code)", fillcolor="#cce0ff"];
  gambit  [label="Gambit\n(C/JS targets)", fillcolor="#cce0ff"];
  kawa    [label="Kawa\n(JVM)", fillcolor="#ddd0ff"];

  // SRFI nodes (R7RS-large building blocks)
  srfi1   [label="SRFI-1\nList library", fillcolor="#fff0cc", shape=note];
  srfi9   [label="SRFI-9\nRecord types", fillcolor="#fff0cc", shape=note];
  srfi69  [label="SRFI-69\nHash tables", fillcolor="#fff0cc", shape=note];
  srfi133 [label="SRFI-133\nVectors", fillcolor="#fff0cc", shape=note];
  srfi158 [label="SRFI-158\nGenerators", fillcolor="#fff0cc", shape=note];

  // Standard lineage
  r5rs -> r7rs_small;
  r7rs_small -> r7rs_large;

  // R7RS-small compliance
  r7rs_small -> guile;
  r7rs_small -> chez;
  r7rs_small -> chibi;
  r7rs_small -> chicken;
  r7rs_small -> gambit;
  r7rs_small -> kawa;

  // R7RS-large SRFI adoption
  r7rs_large -> srfi1;
  r7rs_large -> srfi9;
  r7rs_large -> srfi69;
  r7rs_large -> srfi133;
  r7rs_large -> srfi158;

  // Implementation SRFI support edges (selected)
  srfi9   -> guile   [style=dashed, label="native"];
  srfi9   -> chibi   [style=dashed, label="native"];
  srfi9   -> chicken [style=dashed, label="native"];
  srfi9   -> gambit  [style=dashed, label="native"];
  srfi1   -> guile   [style=dashed];
  srfi1   -> chibi   [style=dashed];
  srfi69  -> guile   [style=dashed, label="native"];
  srfi158 -> chibi   [style=dashed];
  srfi158 -> chicken [style=dashed];
}

diagram-scheme-landscape.png

Source

;; Pairs
(define sh/wal/pair
  (lambda (f r)
    (lambda (op)
      (op f r))))

(define sh/wal/first
  (lambda (p)
    (p (lambda (f r)
         f))))

(define sh/wal/rest
  (lambda (p)
    (p (lambda (f r)
         r))))


;; Lambda Calculus Style
(define sh/wal/switch
  (lambda (f)
    (lambda (r)
      (lambda (op)
        ((op f) r)))))

(define sh/wal/left
  (lambda (a)
    (lambda (b) a)))

(define sh/wal/right
  (lambda (a)
    (lambda (b) b)))

(sh/wal/right ((sh/wal/switch 1) 2))

(define sh/wal/true
  (lambda (a)
    (lambda (b) a)))

(define sh/wal/false
  (lambda (a)
    (lambda (b) b)))

(define sh/wal/not
  (lambda (x)
    ((x sh/wal/false) sh/wal/true)))

(eq? (sh/wal/not sh/wal/true) sh/wal/false)
(eq? (sh/wal/not (sh/wal/not sh/wal/true)) sh/wal/true)
(eq? (sh/wal/not sh/wal/false) sh/wal/true)

(define sh/wal/and
  (lambda (x)
    (lambda (y)
      ((x y) x))))

(define sh/wal/or
  (lambda (x)
    (lambda (y)
      ((x x) y))))


;;  Numbers
(define sh/wal/gcd
  (lambda (a b)
      (cond
       ((= a b) a)
       ((> a b) (sh/wal/gcd (- a b) b))
       ((< a b) (sh/wal/gcd a (- b a))))))


;; Rational Numbers
(define sh/wal/make-rat
  (lambda (n d)
    (sh/wal/pair n d)))

(define sh/wal/numer0
  (lambda (x)
    (sh/wal/first x)))

(define sh/wal/denom0
  (lambda (x)
    (sh/wal/rest x)))

(define half (sh/wal/make-rat 1 2))

(sh/wal/numer0 half)
(sh/wal/denom0 half)


;; TODO Map
(define (sh/wal/map op sequence)
  "Reduce a list to another structure"
  (if (not (null? sequence))
      (cons (op (car sequence))
           (sh/wal/map op (cdr sequence)))))

(define sh/wal/inc
  (lambda (n)
    (+ 1 n)))

(sh/wal/map sh/wal/inc (list 1 2 3 4 5))


;; Reduce
(define (sh/wal/accumulate op initial sequence)
  "Reduce a list to another structure"
  (if (null? sequence)
      initial
      (op (car sequence)
          (sh/wal/accumulate op initial (cdr sequence)))))

(sh/wal/accumulate + 0 (list 1 2 3 4 5))


;; Types
(define twenty-four-seven (sh/wal/make-rat 120 35))

(sh/wal/gcd
 (sh/wal/numer0 twenty-four-seven)
 (sh/wal/denom0 twenty-four-seven))

(define (sh/wal/numer x)
  (let ((g (sh/wal/gcd (sh/wal/first x) (sh/wal/rest x))))
    (/ (sh/wal/first x) g)))

(define (sh/wal/denom x)
  (let ((g (sh/wal/gcd (sh/wal/first x) (sh/wal/rest x))))
    (/ (sh/wal/rest x) g)))

(sh/wal/numer twenty-four-seven)
(sh/wal/denom twenty-four-seven)


;; Sequences
(define (sh/wal/list-ref items n)
  (if (= n 0)
      (sh/wal/first items)
      (sh/wal/list-ref (sh/wal/rest items) (- n 1))))

(define squares (sh/wal/pair
                 1
                 (sh/wal/pair
                  4
                  (sh/wal/pair
                   9
                   (sh/wal/pair
                    16
                    (sh/wal/pair 25 36))))))

(sh/wal/list-ref squares 3)


;; Trees

Reading

  @book{10.5555/230223,
  author = {Friedman, Daniel P. and Felleisen, Matthias},
  title = {The Little Schemer (4th Ed.)},
  year = {1996},
  isbn = {0262560992},
  publisher = {MIT Press},
  address = {Cambridge, MA, USA}
  }

  @book{10.5555/230222,
  author = {Friedman, Daniel P. and Felleisen, Matthias},
  title = {The Seasoned Schemer},
  year = {1996},
  isbn = {026256100X},
  publisher = {MIT Press},
  address = {Cambridge, MA, USA}
  }

  @book{10.5555/547755,
  author = {Abelson, Harold and Sussman, Gerald J.},
  title = {Structure and Interpretation of Computer Programs},
  year = {1996},
  isbn = {0262011530},
  publisher = {MIT Press},
  address = {Cambridge, MA, USA},
  edition = {2nd}
  }

  @book{10.5555/1096473,
  author = {McCarthy, John},
  title = {LISP 1.5 Programmer’s Manual},
  year = {1962},
  isbn = {0262130114},
  publisher = {The MIT Press}
  }

Author: Jason Walsh

j@wal.sh

Last Updated: 2026-04-19 08:30:00

build: 2026-04-20 23:43 | sha: d110973