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 make produces compiled .zo (or platform .so) files; raco exe produces 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 managerraco 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/playgroundracket 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 flowif, 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 & interpolationstring-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 depthTyped 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 highlightsracket/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/interopffi/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 toolstime 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 with datum->syntax, walk via syntax-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 propertiesdefine-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 & ScribblePict (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 VMembedded-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 kernelracket/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-case for 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/hash comprehensions over manual recursion when natural; match over nested if; library code targets racket/base not racket; documentation in Scribble, examples that run.
  • Reviewer focus: missing (provide (contract-out ...)) on public functions; macros that don’t use syntax-parse and thus give terrible errors; mutation where immutable would do; using racket instead of racket/base in libs (slow load); breaking eq?/equal? invariants in structs; failing to set #:transparent for 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/gui is 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 test runs (module+ test ...) submodules, rackcheck (property-based).
  • Docs: Scribble (#lang scribble/manual); render with scribble 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). Use equal? unless you know better. Hash tables ship in eq?/eqv?/equal? flavors — pick to match.
  • Lists are linkedlength is O(n), list-ref is O(n). For random access use vectors.
  • #f is the only false0, '(), "" are all truthy. Coming from JS/Python/Lisp variants this trips.
  • Mutable vs immutable strings/lists/hashesstring-set! errors on a literal string "abc" (immutable); use make-string or string-copy. Same for hashes (hash vs make-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 racket is heavy — multi-second startup. racket/base is much lighter; libraries must target it to be useful in startup-sensitive contexts.
  • define inside a let body — modern Racket allows it (internal defines); let-values doesn’t. Mind the form.
  • Macro hygiene + literal identifierssyntax-parse patterns ~literal foo vs bare foo differ; 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/cc is 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 exe bundles a runtime — standalone exes are ~30MB+. Use raco distribute to package for shipping.
  • Numeric tower nuances(exact? 0.1) is #f; (* 1/3 3) is 1 (exact); (* 0.333 3) is 0.999. Mixing exact and inexact contaminates inexactly.

Citations