Skip to content
Volume S · The TensorCore.si Library

Scheme

A minimal Lisp for thinking clearly.

Scheme is a small, elegant dialect of Lisp created at MIT. Its tiny core and powerful ideas (first-class functions, tail calls, continuations) made it the language of the famous textbook Structure and Interpretation of Computer Programs (SICP). It is a superb language for learning how programming languages and interpreters work.

Prologue

Why learn Scheme?

Scheme has so few rules that you can learn the whole language quickly and then spend your time on ideas.

  • Minimal: a handful of forms build everything else.
  • SICP: one of the most respected computer science books uses it.
  • Interpreters: writing a Scheme interpreter teaches how languages work.
Prologue

Set up and run your first program

Install Chez Scheme (fast) or GNU Guile. Racket can also run Scheme code.

terminal
sudo apt install chezscheme
scheme --script hello.ss
hello.ss
(define name "Ada")
(display (string-append "Hello, AI! Welcome to Scheme, " name "."))
(newline)
TipEvery expression is a list in parentheses with the operator first.
Chapter 1

Expressions and define

Everything is an expression. define names values and functions.

basics.ss
(define layers 32)
(define model "llama3.1")
(define (fp16-gb params) (/ (* params 2) 1e9))
(display model) (newline)
(display (fp16-gb 8.03e9)) (newline)    ; 16.06
(display (+ 1 2 3 4)) (newline)
Chapter 2

Functions and lambda

Functions are values. lambda creates them, and they can be passed to or returned from other functions.

functions.ss
(define (square x) (* x x))
(define (compose f g) (lambda (x) (f (g x))))
(define add1-then-square (compose square (lambda (x) (+ x 1))))
(display (square 12)) (newline)
(display (add1-then-square 4)) (newline)   ; 25
(display (map square '(1 2 3 4))) (newline)
Chapter 3

Lists and recursion

Lists are built with cons; car and cdr take them apart. Recursion is the main way to loop.

lists.ss
(define vram '(("H100" . 80) ("H200" . 141) ("RTX 5090" . 32)))

(define (sum lst)
  (if (null? lst) 0 (+ (car lst) (sum (cdr lst)))))

(define (big-gpus gpus)
  (cond ((null? gpus) '())
        ((>= (cdar gpus) 80) (cons (caar gpus) (big-gpus (cdr gpus))))
        (else (big-gpus (cdr gpus)))))

(display (sum '(80 141 180))) (newline)
(display (big-gpus vram)) (newline)
Chapter 4

Tail calls and let

Scheme guarantees tail-call optimisation, so recursive loops run in constant memory. let creates local variables; a named let makes a loop.

tail.ss
(define (sum-to n)
  (let loop ((i 1) (acc 0))
    (if (> i n) acc (loop (+ i 1) (+ acc i)))))
(display (sum-to 1000000)) (newline)

(let ((w 3) (b 0.5))
  (display (+ (* w 2) b)) (newline))
Chapter 5

A tiny interpreter

Because code is data, a Scheme interpreter for arithmetic fits in a few lines, the classic SICP exercise.

interp.ss
(define (evaluate expr)
  (cond ((number? expr) expr)
        ((pair? expr)
         (let ((op (car expr)) (args (map evaluate (cdr expr))))
           (case op
             ((+) (apply + args))
             ((*) (apply * args))
             ((-) (apply - args))
             (else (error "unknown op" op)))))
        (else (error "bad expression" expr))))

(display (evaluate '(+ 1 (* 2 3) (- 10 4)))) (newline)   ; 13
Part II · AI with Scheme

Scheme and AI

Scheme comes from the MIT AI Lab tradition: symbolic reasoning, search and pattern matching were written in Lisp and Scheme. Today it is used to teach AI fundamentals such as search, logic and automatic differentiation, and GNU Guile can call LLM APIs through its web client.

The essential libraries

GNU Guile web clientHTTP requests from Guile.
guile-jsonJSON for Guile.
Chez Scheme FFICall C libraries.
SICP exercisesClassic symbolic AI problems.

Automatic differentiation with dual numbers

The idea behind backpropagation, in a few lines: carry a value and its derivative together.

autodiff.ss
(define (dual v d) (cons v d))
(define val car) (define der cdr)
(define (d+ a b) (dual (+ (val a) (val b)) (+ (der a) (der b))))
(define (d* a b) (dual (* (val a) (val b))
                       (+ (* (der a) (val b)) (* (val a) (der b)))))

;; f(x) = 3x^2 + 2x  ->  f'(x) = 6x + 2
(define (f x) (d+ (d* (dual 3 0) (d* x x)) (d* (dual 2 0) x)))
(define r (f (dual 4 1)))
(display (list 'value (val r) 'derivative (der r))) (newline)   ; 56 and 26
Part III · Build an AI agent

Call an LLM from Guile Scheme

GNU Guile's built-in web client and the guile-json library can talk to a local model. The tool-calling loop is the same as in other volumes: send, check for tool_calls, reply and repeat.

Your goal→Model plans→Tool call→Your code runs the tool→Result back to model→Final answer
chat.scm
(use-modules (web client) (web uri) (json) (ice-9 receive))

(define url "http://localhost:11434/v1/chat/completions")
(define body
  (scm->json-string
   '(("model" . "llama3.1")
     ("messages" . #((("role" . "user") ("content" . "Explain tensor cores in one sentence.")))))))

(receive (response text)
    (http-post (string->uri url)
               #:body body
               #:headers '((content-type application/json))
               #:decode-body? #t)
  (let* ((reply (json-string->scm (if (string? text) text (utf8->string text))))
         (choice (vector-ref (assoc-ref reply "choices") 0)))
    (display (assoc-ref (assoc-ref choice "message") "content"))
    (newline)))

guile-json represents JSON arrays as vectors and objects as association lists.

Run it locallyInstall Ollama, run ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.
Part IV · Your path

Scheme learning roadmap

Tick items off as you go. Your progress is saved in this browser.

Beginner

Intermediate

Advanced

Part IV · Your path

Projects to build

Calculator interpreterEvaluate nested arithmetic expressions.
Symbolic differentiatorDifferentiate expressions like (* x x).
Mini LispWrite a Scheme interpreter in Scheme.
Reference

Scheme cheat sheet

TaskSyntax
Define(define x 5)
Function(define (f x) (* x 2))
Lambda(lambda (x) (* x 2))
List(list 1 2 3)
Condition(cond ((> x 0) "pos") (else "other"))
Map(map f lst)
Reference

Common mistakes

  • Implementations differ a lot beyond the core standard (R7RS).
  • Use equal? for lists and strings; eq? checks identity.
  • Deep non-tail recursion can still exhaust memory; prefer named let loops.
Reference

Where to go next

On TensorCore.si: Racket volume, Lisp volume.

Keyboard: [ previous language · ] next language · / search the library