Racket — Reference
Source: https://docs.racket-lang.org/
Racket
- Created: 1995 by Matthias Felleisen, Matthew Flatt et al. (originally PLT Scheme; renamed Racket in 2010).
- Latest stable: Racket 9.1 (2026-02). Tracks Chez Scheme as the default backend (Racket CS) since v8.0 (2021).
- Paradigms: Functional-first Lisp/Scheme dialect, supports OO (
racket/class), logic (racklog), lazy (lazy), typed (typed/racket), and language-oriented programming — Racket is a platform for building languages. - Typing: Dynamic by default; Typed Racket provides sound static typing with occurrence typing and gradual interop via contracts.
- Memory: Garbage-collected (Chez’s incremental, generational collector in Racket CS; the older BC variant used Boehm). Native machine code via Chez’s nanopass compiler.
- Compilation: Modules compile to Chez Scheme (Racket CS), which JITs/AOTs to native machine code per platform.
raco makeproduces compiled.zo(or platform.so) files;raco exeproduces standalone executables. - Primary domains: PL research, teaching (HtDP), DSLs and language design, scripting, web apps (Continue/web-server), document/diagram tooling (Scribble, Pict, Slideshow), proof assistants (some Coq/Agda surfaces), education tools (DrRacket).
- Notable runtimes: Racket CS (default since 8.0; built atop Chez Scheme), Racket BC (legacy bytecode + JIT), Racket-on-Chez maintained by the core team.
- Official docs: https://docs.racket-lang.org/
At a glance
Racket is a Scheme descendant repositioned as a language laboratory: every source file begins #lang <name>, and that line literally selects the parser, expander, and runtime semantics. The flagship #lang racket is a batteries-included Lisp with first-class modules, contracts, hygienic macros (syntax-parse), structures with properties, and an OO system. Stewarded by the Racket team across Northeastern, Utah, Indiana, BYU, and others; non-profit foundation since 2018. Famous tagline: “Build a language in a weekend.”
Getting started
Install — Download from https://download.racket-lang.org/. Includes DrRacket (the IDE), racket (CLI/REPL), and raco (the swiss-army CLI). Multi-version: Linux distros usually ship outdated; use the official tarball or Nix.
Hello world (hello.rkt):
#lang racket
(displayln "Hello, world!")Run: racket hello.rkt. Or open in DrRacket and hit Run.
Project layout — Single file is fine. For packages:
my-pkg/
info.rkt ; (define deps '("base" "rackunit-lib"))
main.rkt ; (provide ...)
private/utils.rkt
scribblings/my-pkg.scrbl ; docs (Scribble)
tests/main-test.rkt
A collection (folder of modules with info.rkt) becomes a package when published.
Package manager — raco pkg install <name>, raco pkg update, raco pkg new. Registry: https://pkgs.racket-lang.org/. Most ecosystem packages ship docs hosted at https://docs.racket-lang.org/.
REPL/playground — racket for terminal REPL; DrRacket is the canonical IDE (definitions pane + interactions pane, syntax-aware indent, contract-error highlighting, macro stepper, debugger, profiler). Web playground: https://docs.racket-lang.org/quick/.
Basics
Types/literals — Numbers form a tower: integer → rational → real → complex (exact and inexact, e.g. 1/3 exact, 0.333 inexact, 1+2i complex). Booleans #t/#f (only #f is falsy). Symbols 'foo. Strings "abc" (mutable + immutable variants). Characters #\a. Pairs/lists '(1 2 3) or (list 1 2 3). Vectors #(1 2 3). Hashes (hash 'a 1 'b 2) (immutable) or (make-hash ...) (mutable). Bytes #"abc".
Variables/scoping — Lexically scoped. define introduces a binding (top-level or inside a define-able context); let, let*, letrec, let-values introduce locals; set! mutates. parameterize for dynamic scope on make-parameter cells.
Control flow — if, cond (multi-branch), when/unless (single-arm), case, match (racket/match — full pattern matcher with structs, lists, vectors, predicates, ellipses). Tail calls are optimized (Scheme guarantee).
Functions — Definitions:
(define (greet name) (string-append "Hello, " name))
(define greet2 (lambda (name) ...))Optional + keyword args: (define (f x [y 1] #:k [k 0]) ...). Variadic: (define (sum . xs) (apply + xs)). Multiple return values: (values a b) + define-values. Closures and first-class everywhere.
Strings & interpolation — string-append, format ((format "~a + ~a = ~a" 1 2 3)), ~a, ~v, ~s directives. racket/string provides string-split, string-join, string-contains?. No language-level interpolation; #lang at-exp racket adds Scribble-style @-syntax for embedded expressions.
Collections — Lists are linked, immutable (cons/car/cdr/first/rest). Vectors are random-access. Hashes split by mutability and equality predicate (hash immutable + equal?, hasheq immutable + eq?, make-hash mutable + equal?, etc.). Sets via racket/set. Streams (racket/stream) for lazy sequences.
Intermediate
Type system depth — Typed Racket (#lang typed/racket) is sound, with occurrence typing (refines a binding’s type inside a branch where a predicate succeeded), union types (U Number String), intersection types, polymorphism (All (A) (-> A A)), recursive types, and contract-based interop with untyped modules:
#lang typed/racket
(: factorial (-> Natural Natural))
(define (factorial n) (if (zero? n) 1 (* n (factorial (sub1 n)))))Soundness theorem: a Typed Racket program cannot blame typed code for a contract violation at the typed/untyped boundary.
Modules — Every file is a module. (provide ...) exposes bindings, (require ...) imports. Submodules: (module+ test ...) for inline test modules; raco test foo.rkt runs all test submodules. Phase-distinct: (require (for-syntax racket)) imports for use inside macros.
Error handling — Exceptions: (raise exn), (error 'who "fmt" args ...), with-handlers ([exn:fail? (lambda (e) ...)] ...). Contracts (racket/contract): (provide (contract-out [foo (-> integer? string?)])) enforces at module boundary, blames the violator. Continuations enable richer error strategies (call/cc, prompt, abort).
Concurrency primitives — Green threads via (thread (lambda () ...)) — cooperatively scheduled by Racket’s runtime, not OS threads. Channels for sync rendezvous, async channels (racket/async-channel) for buffered. sync waits on multiple events (events are first-class — (handle-evt ch handler)). Places (racket/place) provide true OS-thread-level parallelism with isolated memory + message passing (the parallelism analog to threads). Futures (racket/future) parallelize purely numeric work.
File I/O & networking — (with-input-from-file path thunk), (call-with-output-file path ... #:exists 'replace). Ports are first-class. Networking: racket/tcp, racket/udp, net/url, net/http-easy (modern HTTP client), openssl for TLS.
Stdlib highlights — racket/base (minimal kernel — what library writers should target), racket/list, racket/string, racket/match, racket/format, racket/contract, racket/class (OO), racket/set, racket/sequence, racket/generator, racket/system, racket/path, racket/file, racket/runtime-path, racket/cmdline, racket/place, racket/future. Plus packages: data/..., math/..., pict, 2htdp/..., web-server.
Advanced
Memory model & GC — Racket CS inherits Chez’s generational collector — major collections are incremental, with very low pause times. (collect-garbage) for explicit, (current-memory-use) reports. racket -W debug-gc@gc traces. racket/place gives separate heaps per place — true parallel heaps without GC contention.
Concurrency deep dive — Custodians own resources (threads, ports, places); (custodian-shutdown-all c) reliably tears down a subsystem. Synchronizable events are the universal abstraction: threads, channels, semaphores, alarm timers, sub-process exits, port readability — all are events and compose with sync. Engines (racket/engine) cap a computation by ticks for cooperative scheduling. Places spin up new OS-level instances of the runtime; communicate via place channels (shareable values: numbers, strings, byte strings, place messages).
FFI/interop — ffi/unsafe is the canonical Racket FFI: load shared libs (ffi-lib), declare types (_int, _string, _pointer, _ptr i _int), bind with get-ffi-obj. Layer define-ffi-definer for per-library helpers. Full callback support (Racket procedures pass-through as C function pointers via _fun). Object lifetime managed via register-finalizer. Embedding the runtime in a host C app via libracketcs.
Reflection — (namespace-mapped-symbols), (eval form ns), (make-base-namespace). Sandboxes (racket/sandbox) eval untrusted code with memory + time limits. Module introspection: (module->imports modpath), (module->exports). Identifiers and syntax objects carry their origin and binding info.
Performance tools — time macro, (profile expr) from profile package (sampling profiler), errortrace for line-precision profiling/coverage, raco profile, (performance-stats). racket/flonum and racket/fixnum give unboxed primitive ops; for/fold with type hints can hit C speed for inner loops.
God mode
Macro system — the crown jewel — Three layers, all hygienic:
define-syntax-rule— pattern → template, simple cases.syntax-rules— multi-clause patterns with ellipses, fully declarative.syntax-case— gives access to the full syntax-object API (manipulate as data, splice unhygienically withdatum->syntax, walk viasyntax-e).syntax-parse(https://docs.racket-lang.org/syntax/stxparse.html) — the modern blessed approach: rich pattern language with syntax classes (reusable parsers with attributes), literal sets, automatic error reporting that names the failed expectation. Powers most modern Racket DSLs.
#lang racket
(require (for-syntax racket/base syntax/parse))
(define-syntax (swap stx)
(syntax-parse stx
[(_ a:id b:id)
#'(let ([tmp a]) (set! a b) (set! b tmp))]))Phase-distinct compilation — Macro code runs at “phase 1” (compile time of the user’s phase 0). (require (for-syntax ...)) brings bindings into phase 1; for-meta n for arbitrary phases. This is what makes Racket macros robust across compilation boundaries — no Common-Lisp-style “macro expanded with the wrong package” surprises.
#lang — build a language in a weekend — A #lang foo directive resolves to foo/lang/reader (a module exporting read and read-syntax) plus a runtime foo module. Implementing both gives you an entire language with its own surface syntax, semantics, error messages, and editor integration. Examples shipped: #lang racket, #lang racket/base, #lang typed/racket, #lang scribble/manual, #lang scribble/lp (literate programming), #lang slideshow, #lang htdp/bsl (teaching levels), #lang lazy, #lang plai, #lang datalog, #lang algol60. See “Beautiful Racket” (Matthew Butterick).
Structure type properties — define-struct/properties attach behaviors to struct types (e.g. make a struct callable with prop:procedure, hashable via prop:equal+hash, sequence-able via prop:sequence, custom-printable via prop:custom-write). The Racket equivalent of Java’s interfaces or Rust’s traits, but on data.
Contracts — first-class, higher-order — (provide (contract-out [f (-> (>=/c 0) string?)])) enforces at module boundary. Higher-order: (-> (-> integer? integer?) integer?) checks each call to the passed function. parametric->/c for safe parametric contracts. Blame is tracked: a violation says which party violated, not just where. Contracts are the soundness mechanism for Typed/Untyped Racket interop.
Units & mixins — Two old-school but still-supported abstraction tools. Units (racket/unit) are first-class modules — declare signatures and units, link them at runtime (an analog of ML functors / OSGi components). Mixins (racket/class) are class-to-class functions for stackable behavior on the OO system.
Typed Racket internals — Implements an extensible type system via metafunctions on the surface syntax. Sound gradual typing: every untyped→typed boundary is wrapped in a runtime contract automatically. The : type syntax compiles to plain Racket plus type-tracking metadata; raco make runs the type checker as a syntax-time pass.
Pict & Scribble — Pict (pict) is a functional 2D-graphics combinator library — (hc-append (circle 50) (rectangle 60 30)) returns a pict value; pict-snip integrates with DrRacket so you can have graphical values in code. Scribble is the documentation language used for the entire Racket reference; #lang scribble/manual lets you write docs as a Racket program.
Embedding the Racket VM — embedded-rkt template + libracketcs headers. Initialize with racket_boot, evaluate via racket_eval. Used by some niche apps to drop a fully-featured Lisp into a C host.
racket/base minimal kernel — racket/base is the small core (~few hundred bindings); racket adds the heavy stack (for/, match, contracts, etc.). Library authors should (require racket/base) to keep startup costs low and avoid circular deps.
Idioms & style
- Naming:
kebab-casefor everything (make-foo,do-bar!).?predicates (zero?,pair?),!mutators (set!,vector-set!),->converters (number->string). - Formatter: fmt (
raco fmt, https://github.com/sorawee/fmt) — community formatter; DrRacket auto-indents but doesn’t reformat. - Linter: review (https://docs.racket-lang.org/review/), macro-debugger (DrRacket built-in) for macro-heavy code; resyntax (https://docs.racket-lang.org/resyntax/) auto-applies refactorings.
- Idiomatic: prefer
(define (f x) ...)over(define f (lambda (x) ...)); structs over hashes for fixed-shape data; contracts at module boundaries;for/list,for/fold,for/hashcomprehensions over manual recursion when natural;matchover nestedif; library code targetsracket/basenotracket; documentation in Scribble, examples that run. - Reviewer focus: missing
(provide (contract-out ...))on public functions; macros that don’t usesyntax-parseand thus give terrible errors; mutation where immutable would do; usingracketinstead ofracket/basein libs (slow load); breakingeq?/equal?invariants in structs; failing to set#:transparentfor debuggability.
Ecosystem
- Web: web-server (built-in, supports continuations for “back-button-safe” apps), koyo (Bogdan Popa’s modern web stack), plisqin/deta (DB query DSLs), racket-spin, racquel (ORM).
- GUI:
racket/guiis the cross-platform native GUI toolkit (wraps Cocoa/GTK/Win32). DrRacket itself is built on it. - Graphics/diagrams: Pict, Slideshow (presentations as Racket programs), 2htdp/image (teaching).
- Math/data:
math(math/array, math/matrix, math/distributions, math/number-theory), plot (2D/3D plots). - DSLs in the wild: Scribble (docs), Slideshow (slides), Pollen (https://pollenpub.com/ — Matthew Butterick’s prose pubs), Beautiful Racket book examples (
#lang basic,#lang stacker), Lindenmayer (#lang lindenmayer). - Testing: RackUnit (xUnit-style,
rackunit-lib),raco testruns(module+ test ...)submodules, rackcheck (property-based). - Docs: Scribble (
#lang scribble/manual); render withscribble file.scrbl; published to https://docs.racket-lang.org/. - Notable users/projects: Hackett (Haskell-like ML in
#lang), Rosette (solver-aided programming, Emina Torlak), PLAI book ecosystem, Bootstrap:World (CS curriculum), Naughty Dog (some scripting historically), Beautiful Racket publishing.
Gotchas
- Two equality predicates (well, three):
eq?(pointer identity),eqv?(numbers + chars + eq?),equal?(deep structural). Useequal?unless you know better. Hash tables ship ineq?/eqv?/equal?flavors — pick to match. - Lists are linked —
lengthis O(n),list-refis O(n). For random access use vectors. #fis the only false —0,'(),""are all truthy. Coming from JS/Python/Lisp variants this trips.- Mutable vs immutable strings/lists/hashes —
string-set!errors on a literal string"abc"(immutable); usemake-stringorstring-copy. Same for hashes (hashvsmake-hash). - Tail calls only in tail position —
(begin (f x))is tail;(+ 1 (f x))is not. Macros that wrap in non-tail expressions silently break TCO. #lang racketis heavy — multi-second startup.racket/baseis much lighter; libraries must target it to be useful in startup-sensitive contexts.defineinside aletbody — modern Racket allows it (internal defines);let-valuesdoesn’t. Mind the form.- Macro hygiene + literal identifiers —
syntax-parsepatterns~literal foovs barefoodiffer; literal matching is by binding, not by name. - Phase confusion — using a runtime function inside a macro without
(require (for-syntax ...))errors at compile time with a “compile-time” message that beginners often misread. call/ccis real and full — not just a one-shot escape. Capturing continuations in resource-acquisition contexts (with-input-from-file) and re-invoking can leak.raco exebundles a runtime — standalone exes are ~30MB+. Useraco distributeto package for shipping.- Numeric tower nuances —
(exact? 0.1)is#f;(* 1/3 3)is1(exact);(* 0.333 3)is0.999. Mixing exact and inexact contaminates inexactly.
Citations
- Official site: https://racket-lang.org/
- Documentation hub: https://docs.racket-lang.org/
- Quick: An Introduction to Racket with Pictures: https://docs.racket-lang.org/quick/
- The Racket Guide: https://docs.racket-lang.org/guide/
- The Racket Reference: https://docs.racket-lang.org/reference/
- Typed Racket Guide: https://docs.racket-lang.org/ts-guide/
- syntax/parse: https://docs.racket-lang.org/syntax/stxparse.html
- Contracts: https://docs.racket-lang.org/guide/contracts.html
- Beautiful Racket (Matthew Butterick): https://beautifulracket.com/
- Pollen: https://pollenpub.com/
- Scribble: https://docs.racket-lang.org/scribble/
- Pict: https://docs.racket-lang.org/pict/
- racket/place: https://docs.racket-lang.org/reference/places.html
- ffi/unsafe: https://docs.racket-lang.org/foreign/
- raco pkg: https://docs.racket-lang.org/pkg/
- Racket package index: https://pkgs.racket-lang.org/
- “Languages as Libraries” (Tobin-Hochstadt et al., PLDI 2011): https://www2.ccs.neu.edu/racket/pubs/pldi11-thacff.pdf
- “Macros that Work Together” (Flatt et al., JFP 2012): https://www.cs.utah.edu/plt/publications/jfp12-fcdf.pdf
- Rosette: https://emina.github.io/rosette/