RacketCon 2026 (sixteenth)
Table of Contents
- 1. Overview
- 2. Schedule
- 3. How the talks relate
- 4. Saturday
- 4.1. On Notation — Pat Hanrahan
- 4.2. Type Inference With Logical Types For Untyped Languages — Fred Fu
- 4.3. Language-Oriented Low-Level Programming with Pille — Lucas Myers
- 4.4. Treason: Making Macros and IDE Services Work Together — Mike Delmonaco
- 4.5. Herbie: Improving Floating-point Accuracy — Pavel Panchekha
- 4.6. Effect Handlers in cio — JJ
- 4.7. BrandX: Commoditizing OOP with Interfaces, Generics, and Contracts — Ryan Culpepper
- 5. Sunday
- 6. Destructuring maps: Racket has it, Guile does not
- 7. Notes
1. Overview
- Date: October 3–4, 2026
- Location: Oakstop, Broadway Gallery Suite, Oakland, California
- URL: https://con.racket-lang.org/
- Streamed live; recordings on YouTube afterwards.
- SPLASH/ISSTA 2026 opens in Oakland on the same day.
Written from the published program on 2026-10-01, before the conference. The examples below were run on Racket 8.15 and Guile 3.0.10. Previous edition: RacketCon 2025.
2. Schedule
2.1. Saturday, October 3
| Time | Speaker | Talk |
|---|---|---|
| 9:00 | Pat Hanrahan | On Notation (keynote) |
| 10:30 | Fred Fu | Type Inference With Logical Types For Untyped Languages |
| 11:10 | Lucas Myers | Language-Oriented Low-Level Programming with Pille |
| 1:30 | Mike Delmonaco | Treason: Making Macros and IDE Services Work Together |
| 2:10 | Pavel Panchekha | Herbie: Improving Floating-point Accuracy |
| 3:30 | JJ | Effect Handlers in cio |
| 4:10 | Ryan Culpepper | BrandX: Commoditizing OOP with Interfaces, Generics, and Contracts |
2.2. Sunday, October 4
| Time | Speaker | Talk |
|---|---|---|
| 9:00 | Sam Phillips | Uke: Immutable Dataframes for Racket |
| 9:40 | Matthew Flatt | A New Foreign-Function Interface: ffi2 |
| 11:00 | Sam Tobin-Hochstadt | The State of Racket |
| 11:30 | Racket Management | Racket Town Hall |
Times are Pacific.
3. How the talks relate
Four threads run through the nine talks, and each has a counterpart at Clojure/conj 2026, held the same week.
| Thread | RacketCon talks | Same question elsewhere |
|---|---|---|
| Reaching native code | Pille, ffi2 | jank on LLVM; babashka's FFI; Panama in Fast, Lean, Native Clojure |
| Types and checks without a rewrite | Type inference, BrandX | Clojure 1.13 checked destructuring; a new protocol implementation |
| Tools that understand macros | Treason | clojure-lsp and clj-kondo; a source inspector for Hiccup |
| Control and data as libraries | cio, Uke, Herbie | core.async; tech.ml.dataset |
The keynote sits above all four: notation is what a language-oriented community is arguing about in every one of them.
4. Saturday
4.1. On Notation — Pat Hanrahan
Keynote. Canon Professor Emeritus at Stanford; led the design of RenderMan at Pixar; co-founded Tableau; 2019 Turing Award. No abstract is published.
RenderMan's shading language and Tableau's visual query language are both notations built for one domain, which is the thing Racket exists to make cheap. The same argument, from the other side, opened Clojure/conj's source inspector talk with Bret Victor.
4.2. Type Inference With Logical Types For Untyped Languages — Fred Fu
PhD candidate at Indiana. The problem: a Typed Racket program imports a macro
from untyped Racket, the macro expands to a lambda, and nobody can annotate
the generated parameters. Local inference does not infer lambda parameters,
so the checker rejects the code. His prototype combines occurrence typing with
algebraic subtyping.
Occurrence typing is the part to have in mind: the type of a variable narrows after a test on it.
#lang typed/racket
(: describe (-> (U String Integer) String))
(define (describe v)
(if (string? v)
(string-append "s:" v) ; v is a String here
(number->string (add1 v)))) ; and an Integer here
(describe "a") ;=> "s:a"
(describe 41) ;=> "42"
| Language | The same idea |
|---|---|
| TypeScript | narrowing by typeof and type guards |
| Kotlin | smart casts |
| Python | isinstance narrowing in mypy and pyright |
| Clojure | no static counterpart; core.typed took occurrence typing from Typed Racket |
The comparison rows are from memory, not checked.
4.3. Language-Oriented Low-Level Programming with Pille — Lucas Myers
PhD student at Northwestern, advised by Robby Findler. Pille grafts Rhombus's enforestation, the step that turns a flat token stream with infix operators into a tree, onto a new core language with an LLVM-based compiler. The macros stay; the Racket VM goes, and with it the runtime that rules out microcontrollers.
This is the same move as jank, presented at Clojure/conj two days earlier: keep the language's metaprogramming, replace the host with LLVM. jank keeps Clojure's runtime semantics and reaches for C++ interop. Pille gives up the runtime to reach the bottom of the stack.
4.4. Treason: Making Macros and IDE Services Work Together — Mike Delmonaco
Software engineer at AWS. Macros get in the way of IDE services when the program is broken or half-typed, because expansion stops at the first error. Treason's expander recovers and keeps collecting. The stated contribution is spec-driven subexpression expansion: syntax-class annotations say which parts of a macro use are expressions, so they can be expanded even when the macro use around them is broken.
Clojure's answer to the same problem is to not expand at all: clj-kondo and clojure-lsp analyse source statically and need a hint for every unfamiliar macro. Treason takes the opposite bet. He spoke last year on a match-like DSL for deep immutable updates.
4.5. Herbie: Improving Floating-point Accuracy — Pavel Panchekha
Associate Professor at Utah. Two ways of writing the same formula can differ enormously in accuracy; Herbie searches for the accurate one. Ten years of writing it in Racket.
The standard example, run here. Both functions compute √(x+1) − √x:
#lang racket
(define (naive x) (- (sqrt (+ x 1)) (sqrt x)))
(define (rewritten x) (/ 1 (+ (sqrt (+ x 1)) (sqrt x))))
(for ([x '(1e0 1e8 1e16)])
(printf "~a naive ~a rewritten ~a\n" x (naive x) (rewritten x)))
1.0 naive 0.41421356237309515 rewritten 0.4142135623730951 100000000.0 naive 5.000000055588316e-5 rewritten 4.9999999874999996e-5 1e+16 naive 0.0 rewritten 5e-9
At 10^16 the naive form returns zero. The subtraction cancels every significant digit; the rewritten form has no subtraction. Herbie finds that rewrite without being told.
4.6. Effect Handlers in cio — JJ
cio is an untyped effect-handler library for Racket and for Guile. An effect handler separates asking for something from deciding what the answer is, and the handler may resume the asker.
The primitive underneath, in Guile, with no library. The program asks; the handler answers twice:
(define tag (make-prompt-tag "ask"))
(define (ask question) (abort-to-prompt tag question))
(define (program) (+ 1 (ask 'how-many)))
(call-with-prompt tag program
(lambda (k question) (list question (k 41) (k 99))))
;;=> (how-many 42 100)
k is the rest of program from the point it asked. Calling it twice is what
exceptions cannot do.
| Language | Nearest thing |
|---|---|
| Racket, Guile | delimited continuations: prompts and abort |
| OCaml 5 | effect handlers in the language |
| Koka, Eff | typed effect handlers |
| Common Lisp | the condition system: handlers that resume, via restarts |
| Clojure | none on the JVM; core.async's go rewrites to a state machine instead |
The comparison rows are from memory. That the library targets Guile as well as Racket is the reason to watch this one from a Guile project.
4.7. BrandX: Commoditizing OOP with Interfaces, Generics, and Contracts — Ryan Culpepper
Racket developer, macros and library design. A new library for OOP through
interfaces and generic functions with proper contract support, motivated by
limits in racket/class and racket/generic.
The neighbours are CLOS generic functions and Clojure protocols. Chris Nuernberger's Clojure/conj talk the day before proposed a replacement protocol implementation for Clojure on similar grounds: the built-in one has limits that only show at scale.
5. Sunday
5.1. Uke: Immutable Dataframes for Racket — Sam Phillips
An opinionated dataframe library: immutable, and careful about copying. Racket already has several dataframes.
The Clojure counterpart is tech.ml.dataset, which is columnar and functional-by-default over typed buffers. The question to carry in: what does "efficient about copying" mean without persistent data structures underneath?
5.2. A New Foreign-Function Interface: ffi2 — Matthew Flatt
Racket's FFI (Eli Barzilay, about 2004) is dynamic and convenient. Chez
Scheme's ftypes (Andy Keep, about 2011) is static and fast, with compact
foreign pointers and cheap checks. ffi2 brings the two together, with small
changes to Chez Scheme.
| System | How it reaches C |
|---|---|
Racket ffi/unsafe |
dynamic, macro-built, no C stub |
| Chez ftypes | static layout known at compile time |
| Guile | (system foreign): dynamic, pointer and bytevector based |
| Clojure / JVM | Panama (java.lang.foreign), final in JDK 22 |
| babashka | its own FFI support, used for libsodium in this week's workshop |
| jank | C++ interop in the language, no binding layer |
This table is the week's theme in one place: three Lisp communities shipped or showed a new way to call native code within five days.
5.3. The State of Racket, and Town Hall
Sam Tobin-Hochstadt, then questions to Racket management. No abstract.
6. Destructuring maps: Racket has it, Guile does not
Not a talk on this year's program. It is here because Clojure 1.13's map destructuring was the subject of the Clojure/conj keynote this week, and the comparison with Racket turns up a gap in Guile.
Racket's match destructures hash tables, with defaults, a rest binding and
an explicit closed mode:
#lang racket
(define order (hash 'id 7 'role 'admin))
(match order [(hash* ['id id] ['name name #:default "anon"] #:rest rest)
(list id name rest)])
;;=> (7 anon #hash((role . admin)))
(match order [(hash* ['id id] #:closed) id])
;; no match: 'role is present and the pattern is closed
Guile's (ice-9 match) has no hash-table pattern. An association list matches
only structurally, so key order matters:
(use-modules (ice-9 match))
(match '((id . 7) (role . admin)) ((('id . id) . rest) (list id rest)))
;;=> (7 ((role . admin)))
(match '((role . admin) (id . 7)) ((('id . id) . rest) id))
;; no match: order matters
(define table (make-hash-table))
(hash-set! table 'id 7)
(match table (_ 'only-wildcard))
;;=> only-wildcard
| Feature | Clojure 1.13 | Racket match |
Guile (ice-9 match) |
|---|---|---|---|
| Bind by key from a map | :keys |
hash*, hash-table |
no |
| Default for an absent key | :or |
#:default |
no |
| Absent key is an error | :keys! |
the default: no match | no |
| The unmentioned entries, as data | :excess |
#:rest |
no |
| Reject unmentioned entries | not offered | #:closed |
no |
| Absent keys collected as data | :missing |
no | no |
The Clojure column is from running 1.13.0-alpha8; the Racket and Guile columns are from the runs above. Racket offers the closed mode Clojure declined to. Neither Racket nor Guile reports what was missing as a value.
That gap is a project: a Guile library giving match keyed patterns over hash
tables and association lists, with defaults and a rest binding as in Racket,
and with the missing-keys-as-data idea from Clojure. It would be a paper for
the Scheme Workshop: what each design chose, and which choices survive on a
Scheme with mutable hash tables and no persistent maps.
7. Notes
- TBD, after the stream.