Racket Programming Mastery: Language-Oriented Programming from First Principles
Goal
Master Racket by building systems that force you to understand its unique “language as library” philosophy, so you can design domain-specific languages, create powerful macros, and leverage the full spectrum of Racket’s capabilities from functional programming to GUI development, web applications, and educational tools. By completing these projects, you will deeply understand how programming languages themselves can be created, extended, and composed as first-class abstractions.
Why Racket Matters
Racket is not just a programming language. It is a programming language laboratory—a platform for creating, studying, and deploying programming languages. Born from the Scheme/Lisp tradition, Racket has evolved into something unique: a practical tool for building production software AND a research vehicle for exploring language design.
The Language-Oriented Programming Paradigm
Most languages give you a fixed set of features. Want pattern matching? Hope the designers included it. Want lazy evaluation? Learn a different language. Want a type system? Use what’s provided or nothing.
Racket flips this: every language feature is a library. Pattern matching, contracts, types, lazy evaluation, logic programming—all are libraries you can import, modify, or replace. More radically, you can create entirely new languages with their own syntax, semantics, and error messages, and these languages interoperate seamlessly with each other.
Traditional Language Model Racket's Model
┌────────────────────────────────┐ ┌────────────────────────────────┐
│ Fixed Language Features │ │ #lang your-language │
├────────────────────────────────┤ ├────────────────────────────────┤
│ Syntax: set by designers │ │ Syntax: you define it │
│ Types: what they give you │ │ Types: import/create yours │
│ Macros: limited or none │ │ Macros: full hygienic system │
│ Errors: generic messages │ │ Errors: custom for your DSL │
│ Interop: compile-time walls │ │ Interop: seamless mixing │
└────────────────────────────────┘ └────────────────────────────────┘
│ │
▼ ▼
One-size-fits-all Language tailored to problem
Historical Context
Racket began as PLT Scheme in 1995 at Rice University, created by Matthias Felleisen and his students. The goal: make programming accessible and rigorous for education while advancing programming language research.
Key milestones:
- 1995: PLT Scheme founded, DrScheme IDE created
- 2001: Macros that work (hygienic macro system matured)
- 2007: “Contracts” introduced for software engineering
- 2010: Renamed to “Racket” to reflect its broader mission
- 2015: Typed Racket reaches maturity
- 2020+: Racket-on-Chez (new runtime for speed)
Real-World Impact
Racket is used in:
- Education: “How to Design Programs” curriculum taught worldwide
- Industry: Naughty Dog (game scripting), Boeing (verification), many startups
- Research: Hundreds of papers on language design, macros, types, contracts
- Tools: DrRacket (award-winning IDE), Scribble (documentation), Pollen (publishing)
What Sets Racket Apart
| Feature | Racket | Common Lisp | Clojure | Haskell |
|---|---|---|---|---|
| #lang extensibility | Core feature | N/A | N/A | N/A |
| Hygienic macros | First-class | Manual | Limited | Template Haskell |
| Contracts | Built-in | Library | spec | N/A |
| IDE support | DrRacket | Varies | Varies | Varies |
| Educational focus | Central | No | No | No |
| Language creation | Trivial | Possible | Hard | Very hard |
Prerequisites & Background Knowledge
Essential Prerequisites
- Basic programming in any language (Python, JavaScript, etc.)
- Recursion comfort - you should be able to trace recursive function calls
- Willingness to embrace parentheses - they’re not bugs, they’re features
- Curiosity about language design - “Why does this language work this way?”
Helpful but Not Required
- Prior Lisp/Scheme experience (will accelerate learning)
- Functional programming concepts (map, filter, reduce)
- Compiler/interpreter basics (lexing, parsing)
- Type theory (for Typed Racket projects)
Self-Assessment Questions
Before starting, can you answer these?
- Recursion: How would you implement factorial without loops?
- First-class functions: What does it mean for functions to be “values”?
- Data structures: How does a linked list differ from an array?
- Syntax vs semantics: What’s the difference between how code looks and what it does?
If you can answer 2+ of these, you’re ready. If not, start with Project 1 anyway—you’ll learn along the way.
Development Environment Setup
# Install Racket
# macOS (Homebrew)
brew install --cask racket
# Linux (Ubuntu/Debian)
sudo apt-get install racket
# Windows
# Download from https://download.racket-lang.org/
# Verify installation
racket --version
# Launch DrRacket (recommended IDE)
drracket
DrRacket Features:
- Syntax highlighting with parenthesis matching
- Step-by-step debugger
- Test coverage visualization
- Built-in documentation browser
- Language-aware error messages
Time Investment
| Your Background | Estimated Time (all projects) |
|---|---|
| New to programming | 6-8 months |
| Experienced programmer, new to Lisp | 3-4 months |
| Lisp/Scheme experience | 2-3 months |
| Racket user wanting depth | 1-2 months |
Core Concept Analysis
S-Expressions: Code as Data
Everything in Racket is an S-expression (symbolic expression). Function calls, data, and even program structure use the same uniform syntax:
S-Expression Structure
┌────────────────────────────────────────────────────────────────────────┐
│ │
│ Atoms Lists │
│ ┌─────────┐ ┌─────────────────────────────────┐ │
│ │ Numbers │ 42, 3.14 │ (operator arg1 arg2 ...) │ │
│ │ Strings │ "hello" │ │ │
│ │ Symbols │ 'foo, 'bar │ (+ 1 2 3) → 6 │ │
│ │ Booleans│ #t, #f │ (if #t "yes" "no") → "yes" │ │
│ └─────────┘ │ (define x 42) → binds x │ │
│ │ (lambda (x) (+ x 1)) → function │ │
│ └─────────────────────────────────┘ │
│ │
│ KEY INSIGHT: Code and data share the SAME structure │
│ This enables macros to transform code as data! │
│ │
└────────────────────────────────────────────────────────────────────────┘
The #lang Directive: Language as Library
Every Racket file begins with #lang. This single line determines the entire language:
#lang Ecosystem
┌────────────────────────────────────────────────────────────────────────┐
│ │
│ #lang racket ; Full-featured Racket │
│ #lang typed/racket ; With static types │
│ #lang lazy ; Lazy evaluation (like Haskell) │
│ #lang datalog ; Logic programming │
│ #lang scribble ; Documentation language │
│ #lang slideshow ; Presentation slides │
│ #lang your-language ; YOUR custom language! │
│ │
│ How #lang works: │
│ │
│ #lang foo The module 'foo/lang/reader' │
│ │ provides a 'read-syntax' function │
│ ▼ that parses the rest of the file │
│ ┌─────────────┐ │
│ │ foo/lang/ │ The result is a module that │
│ │ reader │◄───────#%module-begin transforms │
│ └─────────────┘ according to 'foo' semantics │
│ │ │
│ ▼ │
│ Parsed program │
│ with foo's semantics │
│ │
└────────────────────────────────────────────────────────────────────────┘
Macros: Compile-Time Code Transformation
Racket’s macro system is its superpower. Macros run at compile time, transforming code before it executes:
Macro Expansion Pipeline
┌────────────────────────────────────────────────────────────────────────┐
│ │
│ Source Code │
│ ┌────────────────────────────────────────┐ │
│ │ (unless (empty? lst) │ │
│ │ (displayln "Has elements")) │ │
│ └────────────────────────────────────────┘ │
│ │ │
│ ▼ Macro Expansion │
│ ┌────────────────────────────────────────┐ │
│ │ (if (not (empty? lst)) │ │
│ │ (displayln "Has elements") │ │
│ │ (void)) │ │
│ └────────────────────────────────────────┘ │
│ │ │
│ ▼ Compilation │
│ ┌────────────────────────────────────────┐ │
│ │ Machine code / bytecode │ │
│ └────────────────────────────────────────┘ │
│ │
│ HYGIENIC: Macro-introduced variables don't clash with user code! │
│ │
│ (define-syntax-rule (swap! a b) │
│ (let ([tmp a]) │
│ (set! a b) │
│ (set! b tmp))) │
│ │
│ Even if user has a 'tmp' variable, it won't be captured. │
│ │
└────────────────────────────────────────────────────────────────────────┘
Contracts: Precise Specifications at Runtime
Racket contracts specify what functions expect and guarantee:
Contract System
┌────────────────────────────────────────────────────────────────────────┐
│ │
│ (provide │
│ (contract-out │
│ [sqrt (-> (>=/c 0) (>=/c 0))])) ; non-negative → non-negative │
│ │
│ ┌───────────────┐ Contract ┌───────────────┐ │
│ │ Caller │──────────────▶│ Function │ │
│ │ (sqrt -1) │ │ sqrt │ │
│ └───────────────┘ └───────────────┘ │
│ │ │ │
│ │ Blame: "caller violated │ │
│ │ contract requiring │ │
│ │ non-negative input" │ │
│ ▼ │ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Contract violation! │ │
│ │ sqrt: contract violation │ │
│ │ expected: (>=/c 0) │ │
│ │ given: -1 │ │
│ │ in: the 1st argument of │ │
│ │ (-> (>=/c 0) (>=/c 0)) │ │
│ │ blaming: top-level (caller) │ │
│ └───────────────────────────────────────────────┘ │
│ │
│ Contracts can express: │
│ - Simple predicates: integer?, string? │
│ - Comparisons: (>/c 0), (between/c 0 100) │
│ - Structures: (list/c integer?) │
│ - Functions: (-> integer? integer?) │
│ - Dependent: (->i ([x integer?]) [result (x) (>/c x)]) │
│ │
└────────────────────────────────────────────────────────────────────────┘
Continuations: Capturing “The Rest of the Computation”
Racket provides first-class continuations—the ability to capture “what happens next”:
Continuation Concept
┌────────────────────────────────────────────────────────────────────────┐
│ │
│ (+ 1 (* 2 (call/cc (lambda (k) ...)))) │
│ │
│ When we reach call/cc, the continuation k is: │
│ "Take this value, multiply by 2, add 1" │
│ Represented as: (λ (v) (+ 1 (* 2 v))) │
│ │
│ We can: │
│ - Call k immediately: (k 5) → completes with 11 │
│ - Store k for later: save it, call later │
│ - Call k multiple times: rewind computation! │
│ - Never call k: abort the computation │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Use cases: │ │
│ │ • Exceptions (escape on error) │ │
│ │ • Backtracking (logic programming) │ │
│ │ • Coroutines (cooperative multitasking) │ │
│ │ • Web frameworks (stateless HTTP with stateful code) │ │
│ │ • Generators (yield values, resume later) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────────────┘
Concept Summary Table
| Concept | What You Need to Internalize |
|---|---|
| S-expressions | Code is data. Lists are the universal structure for both programs and information. |
| #lang directive | Languages are libraries. The first line of every file determines its entire semantics. |
| Functions as values | Functions can be passed, returned, stored. Higher-order functions are the norm. |
| Recursion | Iteration is expressed through recursion. Tail calls are optimized to loops. |
| Pattern matching | Destructure data declaratively. match is your primary tool for case analysis. |
| Macros | Transform code at compile time. Think of macros as “functions over syntax trees.” |
| Hygiene | Macro-introduced bindings are automatically kept separate from user bindings. |
| Contracts | Specify behavior precisely. Blame tracking tells you who violated the contract. |
| Continuations | Capture “the rest of computation.” Enable exceptions, backtracking, coroutines. |
| Modules | Organize code into reusable units. Control what’s exported with provide. |
Deep Dive Reading by Concept
Racket Fundamentals
| Concept | Resource |
|---|---|
| S-expressions, lists, recursion | “How to Design Programs” (HtDP) - Part I: Fixed-Size Data |
| Functions and higher-order programming | “How to Design Programs” - Part II: Arbitrarily Large Data |
| Pattern matching | “The Racket Guide” - Section 4.9: Pattern Matching |
| Modules and requires | “The Racket Guide” - Section 6: Modules |
Macros and Metaprogramming
| Concept | Resource |
|---|---|
| Basic macros (define-syntax-rule) | “Fear of Macros” by Greg Hendershott (online tutorial) |
| Hygienic macros (syntax-case) | “The Racket Guide” - Section 16: Macros |
| Full macro system | “Realm of Racket” - Chapter 9: Creating Languages |
| Advanced macro patterns | “Beautiful Racket” by Matthew Butterick |
Language Creation
| Concept | Resource |
|---|---|
| #lang basics | “Beautiful Racket” - Language tutorial |
| Reader extensions | “The Racket Guide” - Section 17.3: Creating Languages |
| Full language implementation | Racket documentation: “Creating Languages” |
Types and Contracts
| Concept | Resource |
|---|---|
| Contracts fundamentals | “The Racket Guide” - Section 7: Contracts |
| Advanced contracts | “The Racket Reference” - Contracts library |
| Typed Racket | “The Typed Racket Guide” |
Quick Start Guide
If you’re feeling overwhelmed, here’s your first 48 hours:
Day 1: Get Comfortable
- Install Racket and open DrRacket
- Type these in the interactions pane:
(+ 1 2 3) (define x 10) (* x x) (define (square n) (* n n)) (square 5) (map square '(1 2 3 4 5)) - Complete Projects 1-2 from this guide
Day 2: Feel the Power
- Work through Project 3 (higher-order functions)
- Read “Fear of Macros” (takes 1-2 hours)
- Try Project 10 (first macro)
Then continue with projects at your own pace.
Project 1: The REPL Calculator - S-Expressions Fundamentals
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Scheme, Common Lisp
- Difficulty: Level 1: Beginner
- Time Estimate: Weekend
- Knowledge Area: Core Language / Syntax
- Main Book: “How to Design Programs” by Felleisen et al.
What You’ll Build
A command-line calculator that reads arithmetic expressions in prefix notation, evaluates them, and prints results. It supports +, -, *, /, expt (power), and nested expressions.
Why It Teaches Racket
This project forces you to embrace prefix notation and S-expressions. You’ll internalize the uniform syntax that makes Racket code manipulable as data. By building a calculator, you’ll understand that evaluation is just recursive traversal of nested lists.
Core Challenges You’ll Face
- Prefix notation (operator before operands) - maps to S-expression syntax
- Nested evaluation (expressions within expressions) - maps to recursive structure
- Type handling (numbers vs symbols vs lists) - maps to Racket’s type system
- REPL loop (read-eval-print) - maps to interactive development
Key Concepts
- S-expressions: “The Racket Guide” - Section 1.2
- Basic evaluation: “How to Design Programs” - Part I
- Pattern matching: “The Racket Guide” - Section 4.9
- Numbers and arithmetic: “The Racket Reference” - Numbers
Prerequisites
None beyond basic programming knowledge.
Real World Outcome
$ racket calculator.rkt
Welcome to the S-Expression Calculator!
Type an expression, or 'quit' to exit.
calc> (+ 1 2 3)
Result: 6
calc> (* 2 (+ 3 4))
Result: 14
calc> (/ (- 100 50) (+ 2 3))
Result: 10
calc> (expt 2 10)
Result: 1024
calc> (+ (* 3 (expt 2 5)) (- 100 32))
Result: 164
calc> quit
Goodbye!
The Core Question You’re Answering
“How does Racket’s uniform syntax enable computation through recursive evaluation of nested structures?”
Concepts You Must Understand First
Before implementing, can you answer these?
- What is the difference between
(+ 1 2)and'(+ 1 2)(with the quote)? - How would you get the first element of a list? The rest?
- What does
(apply + '(1 2 3))do?
Questions to Guide Your Design
Data Representation:
- How will you represent the user’s input?
- How will you distinguish between operators and operands?
Evaluation Strategy:
- How do you evaluate nested expressions?
- What’s the base case (when do you stop recursing)?
Error Handling:
- What if the user enters invalid syntax?
- What about division by zero?
Thinking Exercise
Before writing code, trace the evaluation of (* 2 (+ 3 (- 5 1))):
- Start with the full expression
- Identify what needs to be evaluated first
- Substitute the result
- Continue until you have a single number
Implementation Hints
Hint 1 (Conceptual): Your evaluator is a function that takes an expression and returns a number. The key insight: some expressions ARE numbers (just return them), and others are lists that need recursive evaluation.
Hint 2 (Structure):
(define (evaluate expr)
(cond
[(number? expr) ...] ; Base case
[(list? expr) ...] ; Recursive case
[else ...])) ; Error case
Hint 3 (Operator Dispatch):
Use match or case on the first element to determine the operation. Apply the operation to the recursively evaluated arguments.
Hint 4 (The REPL Loop):
(define (repl)
(display "calc> ")
(let ([input (read)])
(unless (eq? input 'quit)
(displayln (format "Result: ~a" (evaluate input)))
(repl))))
Books That Will Help
| Topic | Book & Chapter |
|---|---|
| Racket basics | “The Racket Guide” - Ch. 2: Racket Essentials |
| Recursive design | “How to Design Programs” - Part I, II |
| Pattern matching | “The Racket Guide” - Section 4.9 |
Common Pitfalls & Debugging
| Problem | Cause | Fix |
|---|---|---|
application: not a procedure |
Trying to call a number as a function | Check if you’re recursively evaluating properly |
| Infinite loop | No base case for recursion | Handle the number? case first |
| Quote appearing in output | Input not being read correctly | Use read not read-line |
Learning Milestones
- Basic arithmetic works - You understand prefix notation
- Nested expressions evaluate - You understand recursive evaluation
- You add a new operator easily - You see the extensibility of the design
- You want to add variables - You’re ready for Project 2!
Project 2: Environment-Based Evaluator - Variables and Scope
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Scheme, JavaScript
- Difficulty: Level 2: Intermediate
- Time Estimate: 1 week
- Knowledge Area: Language Implementation / Environments
- Main Book: “How to Design Programs” by Felleisen et al.
What You’ll Build
Extend Project 1’s calculator to support variable definitions and lookups. Users can define variables with (define x 10) and use them in expressions like (+ x 5).
Why It Teaches Racket
Variable binding is fundamental to programming. By implementing environments (mappings from names to values), you’ll understand how Racket (and most languages) handle scope, shadowing, and lookup. This is also preparation for understanding closures and lexical scope.
Core Challenges You’ll Face
- Environment representation (mapping names to values) - maps to association lists or hash tables
- Lookup algorithm (finding a variable’s value) - maps to scope resolution
- Extending environments (adding new bindings) - maps to define semantics
- Scope rules (which binding is visible where) - maps to lexical scoping
Key Concepts
- Environments: “SICP” (Structure and Interpretation of Computer Programs) - Section 3.2
- Define semantics: “The Racket Guide” - Section 2.5
- Scope: “How to Design Programs” - Part III
Prerequisites
Project 1 completed.
Real World Outcome
calc> (define x 10)
x defined as 10
calc> (define y 20)
y defined as 20
calc> (+ x y)
Result: 30
calc> (define z (* x y))
z defined as 200
calc> (+ x (+ y z))
Result: 230
calc> (define x 100) ; rebinding
x defined as 100
calc> x
Result: 100
The Core Question You’re Answering
“How do programming languages track the association between names and their values?”
Questions to Guide Your Design
Environment Structure:
- Should you use an association list
((x . 10) (y . 20))or a hash table? - How do you handle the initial (empty) environment?
Define Behavior:
- Should
definemodify the existing environment or create a new one? - What happens if you define the same variable twice?
Lookup Semantics:
- What if a variable isn’t defined?
- Should you return an error or a default value?
Implementation Hints
Hint 1 (Environment as List):
(define empty-env '())
(define (extend-env name value env)
(cons (cons name value) env))
(define (lookup name env)
(cond
[(empty? env) (error "Undefined variable:" name)]
[(eq? name (caar env)) (cdar env)]
[else (lookup name (cdr env))]))
Hint 2 (Modified Evaluator):
Your evaluate function now takes two arguments: the expression AND the environment.
Hint 3 (Handling Define): Define is special—it modifies the environment rather than returning a value. Your REPL needs to handle this differently.
Learning Milestones
- Variables can be defined and used - Basic environment works
- Multiple definitions work - Environment extension works
- Undefined variable gives clear error - Error handling works
- You understand why later definitions shadow earlier ones - You grasp scoping
Project 3: Higher-Order Functions Library
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Haskell, ML
- Difficulty: Level 2: Intermediate
- Time Estimate: 1 week
- Knowledge Area: Functional Programming / Abstraction
- Main Book: “How to Design Programs” by Felleisen et al.
What You’ll Build
Implement the core higher-order functions from scratch: my-map, my-filter, my-fold-right, my-fold-left, my-compose, my-curry, and demonstrate their power by solving practical problems.
Why It Teaches Racket
Higher-order functions are the heart of functional programming. By implementing them yourself, you’ll understand that iteration, selection, and accumulation are all patterns that can be abstracted. You’ll see why functions as values are so powerful.
Core Challenges You’ll Face
- Function as argument (passing behavior as data) - maps to first-class functions
- Recursive patterns (how map/filter/fold recurse) - maps to list recursion
- Accumulator pattern (fold-left vs fold-right) - maps to accumulation strategies
- Function composition (building pipelines) - maps to point-free style
Key Concepts
- Higher-order functions: “SICP” - Section 1.3
- Map, Filter, Fold: “The Racket Guide” - Section 3.8
- Currying: “Programming in Haskell” - Chapter 4
Real World Outcome
#lang racket
(require "my-hof.rkt")
; Map: transform each element
(my-map sqr '(1 2 3 4 5))
; => '(1 4 9 16 25)
; Filter: keep elements matching predicate
(my-filter even? '(1 2 3 4 5 6))
; => '(2 4 6)
; Fold-right: combine from the right
(my-fold-right cons '() '(1 2 3))
; => '(1 2 3)
; Fold-left: combine from the left
(my-fold-left cons '() '(1 2 3))
; => '((((() . 1) . 2) . 3))
; Compose: chain functions
(define add1-then-square (my-compose sqr add1))
(add1-then-square 5)
; => 36
; Curry: partial application
(define add (lambda (x y) (+ x y)))
(define add5 ((my-curry add) 5))
(add5 10)
; => 15
; Practical: Sum of squares of even numbers
(my-fold-left + 0
(my-map sqr
(my-filter even? '(1 2 3 4 5 6 7 8 9 10))))
; => 220
The Core Question You’re Answering
“How can we abstract common patterns of computation over collections?”
Implementation Hints
Map (transforms each element):
(define (my-map f lst)
(if (empty? lst)
'()
(cons (f (first lst)) (my-map f (rest lst)))))
Filter (keeps elements satisfying predicate):
(define (my-filter pred lst)
(cond
[(empty? lst) '()]
[(pred (first lst)) (cons (first lst) (my-filter pred (rest lst)))]
[else (my-filter pred (rest lst))]))
Fold-right (combines elements right-to-left):
(define (my-fold-right f init lst)
(if (empty? lst)
init
(f (first lst) (my-fold-right f init (rest lst)))))
Thinking Exercise
Implement my-map and my-filter using only my-fold-right. This shows that fold is the most general list operation!
Learning Milestones
- You implement map from scratch - You understand element-wise transformation
- You implement fold from scratch - You understand the accumulator pattern
- You reimplement map using fold - You see fold’s generality
- You use composition naturally - You think in pipelines
Project 4: Recursive Data Structures - Trees and Graphs
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: OCaml, Haskell
- Difficulty: Level 2: Intermediate
- Time Estimate: 1-2 weeks
- Knowledge Area: Data Structures / Recursive Design
- Main Book: “How to Design Programs” by Felleisen et al.
What You’ll Build
Implement binary trees and directed graphs using Racket structs. Create operations for traversal, search, insertion, and tree/graph algorithms (depth-first, breadth-first, path finding).
Why It Teaches Racket
Racket’s struct system and pattern matching make tree/graph algorithms elegant. You’ll see how recursive data structures naturally lead to recursive algorithms. This project reinforces the design recipe from HtDP.
Core Challenges You’ll Face
- Struct definitions (defining tree/graph node types) - maps to Racket structs
- Recursive traversal (processing all nodes) - maps to structural recursion
- Accumulator for BFS (using queues) - maps to tail recursion with accumulator
- Cycle detection (graphs can have cycles) - maps to mutable state or visited set
Key Concepts
- Structs: “The Racket Guide” - Section 5
- Recursive data design: “How to Design Programs” - Part IV
- Graph algorithms: “Algorithms” by Sedgewick - Graph chapter
Real World Outcome
#lang racket
; Binary tree definition
(struct tree (value left right) #:transparent)
(define leaf 'leaf)
; Example tree:
; 5
; / \
; 3 8
; / \ \
; 1 4 9
(define my-tree
(tree 5
(tree 3 (tree 1 leaf leaf) (tree 4 leaf leaf))
(tree 8 leaf (tree 9 leaf leaf))))
; In-order traversal
(tree-inorder my-tree)
; => '(1 3 4 5 8 9)
; Search
(tree-contains? my-tree 4)
; => #t
(tree-contains? my-tree 7)
; => #f
; Insert (BST property maintained)
(tree-inorder (tree-insert my-tree 6))
; => '(1 3 4 5 6 8 9)
; Graph definition
(struct graph (nodes edges) #:transparent)
(define my-graph
(graph '(a b c d e)
'((a b) (a c) (b d) (c d) (d e))))
; DFS from 'a
(graph-dfs my-graph 'a)
; => '(a b d e c)
; BFS from 'a
(graph-bfs my-graph 'a)
; => '(a b c d e)
; Path finding
(graph-path my-graph 'a 'e)
; => '(a b d e)
The Core Question You’re Answering
“How do you design algorithms that mirror the structure of their data?”
Implementation Hints
Hint 1 (Tree Traversal with Pattern Matching):
(define (tree-inorder t)
(match t
['leaf '()]
[(tree v l r)
(append (tree-inorder l) (list v) (tree-inorder r))]))
Hint 2 (BFS needs a queue): Use a list as a queue. Add to the end, remove from the front.
Hint 3 (Graph representation): An edge list is simple but slow. For efficiency, use adjacency lists (hash of node to list of neighbors).
Learning Milestones
- Tree operations work - You understand structural recursion
- Graph traversals work - You understand accumulator-based algorithms
- Path finding works - You understand backtracking or BFS
- You add a new algorithm easily - The design recipe works!
Project 5: Pattern Matching Expression Transformer
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: ML, Haskell
- Difficulty: Level 2: Intermediate
- Time Estimate: 1 week
- Knowledge Area: Pattern Matching / Code Transformation
- Main Book: “The Racket Guide”
What You’ll Build
A source-to-source transformer that takes mathematical expressions and transforms them according to algebraic rules. For example, (* x 0) becomes 0, (+ x 0) becomes x, and (* 2 (+ a b)) becomes (+ (* 2 a) (* 2 b)) (distribution).
Why It Teaches Racket
Pattern matching is one of Racket’s most powerful features. This project shows how code can be treated as data, matched against patterns, and transformed. This is a stepping stone to understanding macros.
Core Challenges You’ll Face
- Pattern syntax (how to express algebraic rules) - maps to match patterns
- Recursive transformation (transforming nested expressions) - maps to recursive matching
- Rule application order (which rule to apply first) - maps to transformation strategy
- Fixed-point iteration (apply rules until no change) - maps to normalization
Key Concepts
- Match: “The Racket Guide” - Section 4.9
- Symbolic computation: “SICP” - Section 2.3.2
- Term rewriting: Academic papers on rewrite systems
Real World Outcome
#lang racket
(require "algebra.rkt")
; Identity rules
(simplify '(+ x 0))
; => 'x
(simplify '(* x 1))
; => 'x
; Zero rules
(simplify '(* x 0))
; => 0
; Constant folding
(simplify '(+ 2 3))
; => 5
; Nested simplification
(simplify '(+ (* x 0) (* y 1)))
; => 'y
; Distribution
(distribute '(* 2 (+ a b)))
; => '(+ (* 2 a) (* 2 b))
; Full normalization
(normalize '(* (+ 1 1) (+ x 0)))
; => '(* 2 x)
The Core Question You’re Answering
“How can we transform symbolic expressions by pattern matching and substitution?”
Implementation Hints
Hint 1 (Basic Rule with Match):
(define (simplify-step expr)
(match expr
; Identity rules
[`(+ ,x 0) x]
[`(+ 0 ,x) x]
[`(* ,x 1) x]
[`(* 1 ,x) x]
; Zero rules
[`(* ,x 0) 0]
[`(* 0 ,x) 0]
; Constant folding
[`(+ ,(? number? a) ,(? number? b)) (+ a b)]
[`(* ,(? number? a) ,(? number? b)) (* a b)]
; Recursive case
[`(,op ,args ...) `(,op ,@(map simplify args))]
; Base case
[_ expr]))
Hint 2 (Fixed-point simplification):
(define (simplify expr)
(let ([simplified (simplify-step expr)])
(if (equal? simplified expr)
expr
(simplify simplified))))
Learning Milestones
- Basic rules work - You understand pattern matching
- Nested expressions simplify - You understand recursive transformation
- Rules apply exhaustively - You understand fixed-point iteration
- You add new rules easily - The pattern is extensible
Project 6: Contract-Based Library Design
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: None (Racket-specific)
- Difficulty: Level 2: Intermediate
- Time Estimate: 1 week
- Knowledge Area: Software Engineering / Contracts
- Main Book: “The Racket Guide”
What You’ll Build
A data structure library (stack, queue, priority queue) with full contract specifications. Every function has precise contracts describing its inputs, outputs, and invariants. Violations produce clear blame messages.
Why It Teaches Racket
Contracts are Racket’s answer to “how do we specify what code should do?” Unlike types (which constrain structure), contracts constrain behavior at runtime with rich predicates. Blame tracking tells you who violated the contract—essential for debugging.
Core Challenges You’ll Face
- Designing contracts (what properties to specify) - maps to contract language
- Blame assignment (who’s responsible for violations) - maps to positive/negative blame
- Invariant preservation (ensuring data structure properties) - maps to dependent contracts
- Performance considerations (contracts have overhead) - maps to contract boundaries
Key Concepts
- Contract basics: “The Racket Guide” - Section 7
- Dependent contracts: “The Racket Reference” - Contract library
- Blame: Academic papers on contract semantics
Real World Outcome
#lang racket
(require "contracted-stack.rkt")
; Create a stack
(define s (make-stack))
; Push returns the new stack
(define s2 (push s 10))
; Pop returns value and new stack
(define-values (val s3) (pop s2))
val ; => 10
; Contract violation: popping empty stack
(pop s)
; => pop: contract violation
; expected: (not/c stack-empty?)
; given: (stack '())
; blaming: top-level (the caller)
; in: the 1st argument of
; (-> (not/c stack-empty?)
; (values any/c stack?))
; Contract violation: pushing non-number on number-stack
(require "contracted-number-stack.rkt")
(define ns (make-number-stack))
(push ns "hello")
; => push: contract violation
; expected: number?
; given: "hello"
; blaming: top-level (the caller)
The Core Question You’re Answering
“How can we specify and enforce behavioral properties at module boundaries?”
Implementation Hints
Hint 1 (Basic Contract):
(provide
(contract-out
[push (-> stack? any/c stack?)]
[pop (-> (not/c stack-empty?) (values any/c stack?))]
[stack-empty? (-> stack? boolean?)]))
Hint 2 (Dependent Contract for Invariant):
; A sorted-list contract
(define sorted-list/c
(flat-named-contract
'sorted-list
(lambda (lst)
(or (empty? lst)
(empty? (rest lst))
(and (<= (first lst) (second lst))
(sorted-list/c (rest lst)))))))
Learning Milestones
- Basic contracts work - You understand contract syntax
- Blame points to the right party - You understand positive/negative blame
- Invariants are preserved - You understand dependent contracts
- You design contracts naturally - Contracts are part of your design process
Project 7: Typed Racket Calculator
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Typed Racket
- Alternative Programming Languages: Haskell, OCaml
- Difficulty: Level 3: Advanced
- Time Estimate: 1 week
- Knowledge Area: Type Systems / Gradual Typing
- Main Book: “The Typed Racket Guide”
What You’ll Build
Reimplement the calculator from Projects 1-2 using Typed Racket. Define types for expressions, environments, and values. Experience how the type checker catches bugs at compile time.
Why It Teaches Racket
Typed Racket is a gradually typed sibling of Racket. Learning it shows how types can coexist with dynamic code. You’ll see the tradeoffs between static type checking and dynamic flexibility, and learn to interoperate typed and untyped code.
Core Challenges You’ll Face
- Type definitions (defining types for your data) - maps to algebraic data types
- Type annotations (telling the type checker what you mean) - maps to type syntax
- Occurrence typing (type refinement in conditionals) - maps to flow-sensitive typing
- Typed/untyped interaction (require/typed) - maps to gradual typing boundaries
Key Concepts
- Typed Racket basics: “The Typed Racket Guide” - Getting Started
- Occurrence typing: “The Typed Racket Guide” - Section 2
- Gradual typing: Academic papers on gradual types
Real World Outcome
#lang typed/racket
(define-type Expr
(U Number
Symbol
(List '+ Expr Expr)
(List '* Expr Expr)
(List 'let (List Symbol Expr) Expr)))
(define-type Env (Listof (Pairof Symbol Number)))
(: evaluate (-> Expr Env Number))
(define (evaluate expr env)
(cond
[(number? expr) expr]
[(symbol? expr) (lookup expr env)]
[(eq? (first expr) '+)
(+ (evaluate (second expr) env)
(evaluate (third expr) env))]
[(eq? (first expr) '*)
(* (evaluate (second expr) env)
(evaluate (third expr) env))]
[(eq? (first expr) 'let)
(match-define (list 'let (list var val-expr) body) expr)
(define val (evaluate val-expr env))
(evaluate body (cons (cons var val) env))]))
(: lookup (-> Symbol Env Number))
(define (lookup name env)
(cond
[(empty? env) (error "Undefined:" name)]
[(eq? name (caar env)) (cdar env)]
[else (lookup name (cdr env))]))
; Type error caught at compile time!
; (evaluate "not-an-expr" '())
; => Type Checker: type mismatch
; expected: Expr
; given: String
The Core Question You’re Answering
“How do static types help prevent bugs while preserving Racket’s expressiveness?”
Implementation Hints
Hint 1 (Union Types for AST):
(define-type Expr
(U Number Symbol (List Symbol Expr ...)))
Hint 2 (Occurrence Typing):
After (number? expr) returns true in a cond branch, the type checker knows expr is Number in that branch.
Hint 3 (Typed/Untyped Interaction):
(require/typed "untyped-lib.rkt"
[some-function (-> Number String)])
Learning Milestones
- Code type-checks - You understand basic type annotations
- Type errors give helpful messages - You understand how types catch bugs
- You use union types effectively - You understand algebraic data types
- You interface typed/untyped code - You understand gradual typing
Project 8: GUI Application with racket/gui
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Python (Tkinter), JavaScript (Electron)
- Difficulty: Level 2: Intermediate
- Time Estimate: 1-2 weeks
- Knowledge Area: GUI Programming / Event-Driven Design
- Main Book: “The Racket Graphical Interface Toolkit”
What You’ll Build
A simple drawing application with racket/gui. Users can draw shapes (rectangles, circles, lines), choose colors, save/load drawings, and undo/redo actions.
Why It Teaches Racket
GUI programming demonstrates event-driven and object-oriented patterns in Racket. You’ll use classes, methods, and event callbacks—a different paradigm from pure functional programming. This shows Racket’s multi-paradigm nature.
Core Challenges You’ll Face
- Class and object system (Racket’s OOP) - maps to racket/class
- Event handling (responding to clicks/keys) - maps to callbacks
- Canvas drawing (rendering graphics) - maps to dc<%> interface
- State management (undo/redo) - maps to command pattern
Key Concepts
- racket/gui: “The Racket Graphical Interface Toolkit”
- Classes: “The Racket Guide” - Section 13
- Drawing contexts: “The Racket Drawing Toolkit”
Real World Outcome
┌──────────────────────────────────────────────────────────────┐
│ Simple Draw [File ▼] [Edit ▼] [Shape ▼] [Color ▼] │
├──────────────────────────────────────────────────────────────┤
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ Line │ │ Rect │ │Circle│ │ Free │ Color: [■] Blue │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ │ │
│ ● │ ● │ │
│ / \ │ │ │
│ / \ └─────────────┘ │
│ / \ │
│ │
│ │
│ │
├──────────────────────────────────────────────────────────────┤
│ Status: Rectangle selected | Shapes: 3 | [Undo] [Redo] │
└──────────────────────────────────────────────────────────────┘
The Core Question You’re Answering
“How do you structure interactive graphical applications in Racket?”
Implementation Hints
Hint 1 (Basic Window):
#lang racket/gui
(define frame (new frame% [label "Simple Draw"] [width 800] [height 600]))
(define canvas
(new canvas%
[parent frame]
[paint-callback
(lambda (canvas dc)
(send dc draw-rectangle 50 50 100 100))]))
(send frame show #t)
Hint 2 (Mouse Events):
(define my-canvas%
(class canvas%
(super-new)
(define/override (on-event event)
(when (send event button-down?)
(printf "Click at ~a, ~a~n"
(send event get-x)
(send event get-y))))))
Hint 3 (Undo/Redo with Command Pattern): Store drawing commands in a list. Undo pops from the list; redo pushes back.
Learning Milestones
- Window appears with canvas - You understand GUI basics
- Shapes draw on click - You understand events
- Undo/redo works - You understand state management
- You can save/load - You understand serialization
Project 9: Web Application with web-server
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Python (Flask), JavaScript (Express)
- Difficulty: Level 3: Advanced
- Time Estimate: 2 weeks
- Knowledge Area: Web Development / Continuations
- Main Book: “Web Applications in Racket”
What You’ll Build
A web-based to-do list application using Racket’s web-server library. Users can add, complete, and delete tasks. The application uses continuations to manage stateful interactions across HTTP requests.
Why It Teaches Racket
Racket’s web-server uses continuations to solve the “stateless HTTP” problem elegantly. Instead of manually managing sessions, you write stateful code that automatically survives across requests. This is a unique and powerful approach to web development.
Core Challenges You’ll Face
- Servlet structure (defining request handlers) - maps to web-server/servlet
- HTML generation (producing web pages) - maps to X-expressions
- Continuations for state (send/suspend, send/forward) - maps to continuation-passing
- Form handling (extracting user input) - maps to request bindings
Key Concepts
- Web server basics: “Web Applications in Racket”
- X-expressions: Racket’s HTML representation
- Continuations in web: “Seaside” (Smalltalk) and Racket’s approach
Real World Outcome
Browser at localhost:8000
┌────────────────────────────────────────────────────────────┐
│ My To-Do List │
├────────────────────────────────────────────────────────────┤
│ │
│ [ ] Buy groceries [Complete][X] │
│ [✓] Finish Racket project [Complete][X] │
│ [ ] Read SICP chapter 4 [Complete][X] │
│ │
│ ┌────────────────────────────────────────┐ │
│ │ Add new task: │ │
│ │ [_________________________________] │ │
│ │ [Add Task] │ │
│ └────────────────────────────────────────┘ │
│ │
│ Completed: 1/3 │
│ │
└────────────────────────────────────────────────────────────┘
The Core Question You’re Answering
“How do continuations enable stateful web applications despite HTTP’s statelessness?”
Implementation Hints
Hint 1 (Basic Servlet):
#lang web-server/insta
(define (start request)
(response/xexpr
`(html
(head (title "Hello"))
(body (h1 "Hello, World!")))))
Hint 2 (Continuation for Multi-Page Flow):
(define (start request)
(define name
(send/suspend
(lambda (k-url)
(response/xexpr
`(html
(body
(form ([action ,k-url])
(input ([name "user"]))
(input ([type "submit"])))))))))
(define user (extract-binding/single 'user (request-bindings name)))
(response/xexpr
`(html (body (h1 "Hello, " ,user "!")))))
Hint 3 (State Across Requests):
The continuation captures the entire execution state. When the user submits the form, execution resumes right where send/suspend was called.
Learning Milestones
- Basic page serves - You understand servlet structure
- Forms work - You understand request handling
- Multi-page flow without sessions - You understand continuations
- You appreciate the elegance - Continuations make web programming natural
Project 10: Your First Macro - Syntax Extensions
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Common Lisp (different macro system)
- Difficulty: Level 3: Advanced
- Time Estimate: 1-2 weeks
- Knowledge Area: Metaprogramming / Macros
- Main Book: “Fear of Macros” by Greg Hendershott
What You’ll Build
Implement several useful macros: unless, when-let, with-logging, define-struct-with-contract, and understand how they transform code at compile time.
Why It Teaches Racket
Macros are Racket’s superpower. They let you extend the language with new syntax. Unlike functions (which receive values), macros receive source code and produce transformed source code. This is the foundation for building domain-specific languages.
Core Challenges You’ll Face
- Macro basics (define-syntax-rule) - maps to simple pattern macros
- Syntax patterns (matching code structure) - maps to syntax-case
- Hygiene (avoiding variable capture) - maps to automatic renaming
- Debugging macros (seeing expansion) - maps to expand and macro-debugger
Key Concepts
- Basic macros: “Fear of Macros” - Sections 1-3
- Hygiene: “The Racket Guide” - Section 16.2
- syntax-case: “The Racket Reference” - Syntax transformers
Real World Outcome
#lang racket
(require "my-macros.rkt")
; unless: execute when condition is false
(unless (empty? lst)
(displayln "List has elements"))
; Expands to:
; (if (not (empty? lst))
; (begin (displayln "List has elements"))
; (void))
; when-let: bind and conditionally execute
(when-let ([x (assoc 'key data)])
(displayln (format "Found: ~a" x)))
; Expands to:
; (let ([x (assoc 'key data)])
; (when x
; (displayln (format "Found: ~a" x))))
; with-logging: wrap code with timing
(with-logging "database-query"
(query db "SELECT * FROM users"))
; Expands to:
; (let ([start (current-milliseconds)])
; (begin0
; (query db "SELECT * FROM users")
; (printf "database-query took ~a ms~n"
; (- (current-milliseconds) start))))
The Core Question You’re Answering
“How can we extend Racket’s syntax with new language constructs?”
Implementation Hints
Hint 1 (Simple Pattern Macro):
(define-syntax-rule (unless condition body ...)
(if (not condition)
(begin body ...)
(void)))
Hint 2 (Multiple Patterns with syntax-rules):
(define-syntax when-let
(syntax-rules ()
[(_ ([var expr]) body ...)
(let ([var expr])
(when var body ...))]))
Hint 3 (Debugging with expand):
(require macro-debugger/expand)
(expand-only #'(unless #t 1) (list #'unless))
Learning Milestones
- Simple macro works - You understand define-syntax-rule
- You avoid variable capture - You understand hygiene
- Complex patterns work - You understand syntax-rules/syntax-case
- You use the macro debugger - You can debug expansion issues
Project 11: Parser Combinator Library
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Haskell (Parsec), F# (FParsec)
- Difficulty: Level 3: Advanced
- Time Estimate: 2 weeks
- Knowledge Area: Parsing / Functional Patterns
- Main Book: “Parsec” documentation (Haskell, for concepts)
What You’ll Build
A parser combinator library where parsers are functions that can be combined with operators like seq (sequence), alt (alternative), many (repetition), and map-parser (transformation). Use it to parse arithmetic expressions, JSON, or a simple programming language.
Why It Teaches Racket
Parser combinators demonstrate the power of higher-order functions and monadic composition. Parsers are just functions, and combining them creates more complex parsers. This is a beautiful application of functional programming principles.
Core Challenges You’ll Face
- Parser as function (input -> result + remaining input) - maps to function representation
- Combinator design (seq, alt, many) - maps to higher-order composition
- Error reporting (what went wrong, where) - maps to error handling
- Backtracking (trying alternatives) - maps to choice semantics
Key Concepts
- Parser combinators: “Monadic Parser Combinators” paper by Hutton & Meijer
- Monads: “Learn You a Haskell” - Monad chapter (for conceptual understanding)
- Regular expressions vs parsing: Understanding the difference
Real World Outcome
#lang racket
(require "parser-combinators.rkt")
; Define primitives
(define digit (char-satisfies char-numeric?))
(define letter (char-satisfies char-alphabetic?))
; Combine into larger parsers
(define integer
(map-parser (compose string->number list->string)
(many1 digit)))
(define identifier
(map-parser list->string
(seq letter (many (alt letter digit)))))
(define expr
(alt (seq integer (char #\+) (lazy expr)
(lambda (a _ b) (list '+ a b)))
integer))
; Use it
(parse integer "123abc")
; => (success 123 "abc")
(parse identifier "foo123 bar")
; => (success "foo123" " bar")
(parse expr "1+2+3")
; => (success '(+ 1 (+ 2 3)) "")
; Error case
(parse integer "abc")
; => (failure "Expected digit" "abc")
The Core Question You’re Answering
“How can we build complex parsers by combining simple ones?”
Implementation Hints
Hint 1 (Parser Type):
; A parser is a function: String -> (or Success Failure)
(struct success (value rest) #:transparent)
(struct failure (message input) #:transparent)
(define (char-satisfies pred)
(lambda (input)
(if (and (not (string-empty? input))
(pred (string-ref input 0)))
(success (string-ref input 0) (substring input 1))
(failure "No match" input))))
Hint 2 (Sequence Combinator):
(define (seq p1 p2 combine)
(lambda (input)
(match (p1 input)
[(success v1 rest1)
(match (p2 rest1)
[(success v2 rest2) (success (combine v1 v2) rest2)]
[failure failure])]
[failure failure])))
Hint 3 (Alternative Combinator):
(define (alt p1 p2)
(lambda (input)
(match (p1 input)
[(success v rest) (success v rest)]
[(failure _ _) (p2 input)])))
Learning Milestones
- Basic parsers work - You understand parser representation
- Combinators compose - You understand higher-order patterns
- You parse arithmetic - You handle recursive grammars
- You parse JSON - The library is practical
Project 12: 2D Game with Graphics and Animation
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Python (pygame), JavaScript (canvas)
- Difficulty: Level 2: Intermediate
- Time Estimate: 2-3 weeks
- Knowledge Area: Game Development / Animation
- Main Book: “Realm of Racket”
What You’ll Build
A simple 2D game (like Snake, Pong, or a platformer) using Racket’s 2htdp/universe and 2htdp/image libraries. The game will have animated graphics, keyboard input, collision detection, and game state management.
Why It Teaches Racket
Games force you to think in terms of state transformations over time. The “big-bang” model (world-state + handlers) is a perfect functional approach to interactive programs. This is the pedagogical approach from “How to Design Programs.”
Core Challenges You’ll Face
- World state design (what data represents the game) - maps to data definitions
- Tick handlers (updating state each frame) - maps to pure transformations
- Rendering (converting state to images) - maps to 2htdp/image
- Input handling (keyboard/mouse) - maps to event handlers
Key Concepts
- Big-bang model: “How to Design Programs” - Part III
- Game loops: “Realm of Racket” - Chapters 3-6
- State machines: Understanding game states (menu, playing, paused, game-over)
Real World Outcome
┌────────────────────────────────────────────────────────────────┐
│ SNAKE GAME │
├────────────────────────────────────────────────────────────────┤
│ │
│ Score: 42 High: 100 │
│ │
│ ┌──────────────────────────────────────────────────────────┐│
│ │ ││
│ │ ● ││
│ │ ││
│ │ ████ ││
│ │ █ ││
│ │ █ ││
│ │ ████████ ││
│ │ ││
│ │ ◆ ││
│ │ ││
│ └──────────────────────────────────────────────────────────┘│
│ │
│ Controls: Arrow keys to move, P to pause, Q to quit │
│ │
└────────────────────────────────────────────────────────────────┘
The Core Question You’re Answering
“How do you model interactive simulations as pure state transformations?”
Implementation Hints
Hint 1 (Basic Big-Bang):
#lang racket
(require 2htdp/image 2htdp/universe)
(struct world (x y) #:transparent)
(define (render w)
(place-image (circle 10 "solid" "red")
(world-x w) (world-y w)
(empty-scene 400 300)))
(define (tick w)
(world (+ (world-x w) 1) (world-y w)))
(define (key w ke)
(cond
[(key=? ke "up") (world (world-x w) (- (world-y w) 10))]
[(key=? ke "down") (world (world-x w) (+ (world-y w) 10))]
[else w]))
(big-bang (world 200 150)
[to-draw render]
[on-tick tick]
[on-key key])
Hint 2 (Snake State):
(struct snake (direction body) #:transparent)
; body is a list of positions, head first
; direction is 'up, 'down, 'left, 'right
Hint 3 (Collision Detection): Check if the head position equals any food position or any body segment.
Learning Milestones
- Something moves on screen - You understand big-bang basics
- Input controls movement - You understand key handlers
- Collision works - You understand position checking
- Full game loop works - You understand game state machines
Project 13: Domain-Specific Language for Configuration
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Ruby (internal DSL), Lua
- Difficulty: Level 3: Advanced
- Time Estimate: 2 weeks
- Knowledge Area: Language Design / DSLs
- Main Book: “Beautiful Racket” by Matthew Butterick
What You’ll Build
A custom configuration language (like YAML but with expressions, conditionals, and includes). Users write configuration files in your DSL syntax, which compiles to Racket data structures.
Why It Teaches Racket
This is your first real #lang. You’ll implement a reader that parses your custom syntax and a #%module-begin that transforms it into executable Racket. This demonstrates Racket’s “language as library” philosophy.
Core Challenges You’ll Face
- Custom syntax (designing your config format) - maps to reader implementation
- Module transformation (#%module-begin) - maps to language semantics
- Error messages (meaningful errors for your DSL) - maps to syntax-error
- Interop with Racket (using your config from Racket) - maps to provide/require
Key Concepts
- Creating #lang: “Beautiful Racket” - Language tutorial
- Readers: “The Racket Reference” - Reader extensions
- Syntax objects: “The Racket Reference” - Syntax model
Real World Outcome
; file: server-config.myconf
#lang my-config
server {
host = "localhost"
port = if (production?) 443 else 8080
database {
url = "postgres://localhost/mydb"
pool-size = 10
}
include "common-settings.myconf"
}
; Using from Racket:
#lang racket
(require "server-config.myconf")
config
; => '(server
; (host "localhost")
; (port 8080)
; (database
; (url "postgres://localhost/mydb")
; (pool-size 10)))
The Core Question You’re Answering
“How do you create a custom language that compiles to Racket?”
Implementation Hints
Hint 1 (Reader Module Structure):
my-config/
lang/
reader.rkt ; Provides read-syntax
main.rkt ; #%module-begin and bindings
Hint 2 (Basic Reader):
; my-config/lang/reader.rkt
#lang racket
(provide read-syntax)
(define (read-syntax src port)
(define content (port->string port))
; Parse content into syntax objects
; Return a module form
(datum->syntax #f
`(module my-config-module my-config/main
,@(parse-config content))))
Hint 3 (#%module-begin):
; my-config/main.rkt
#lang racket
(provide (rename-out [my-module-begin #%module-begin])
config)
(define config-data '())
(define-syntax-rule (my-module-begin body ...)
(#%module-begin
(set! config-data (list body ...))
(define config config-data)
(provide config)))
Learning Milestones
- #lang loads - Your reader is recognized
- Simple configs parse - Your parser works
- Expressions evaluate - Your semantics work
- You use it from Racket - Interop works
Project 14: Educational IDE Mini-Project
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Python (IDLE)
- Difficulty: Level 3: Advanced
- Time Estimate: 2-3 weeks
- Knowledge Area: Educational Tools / IDE Design
- Main Book: DrRacket source code, “How to Design Programs”
What You’ll Build
A simplified teaching IDE (like a mini-DrRacket) with syntax highlighting, a stepper (showing evaluation step-by-step), and helpful error messages for beginners.
Why It Teaches Racket
DrRacket itself is written in Racket. By building a simplified version, you’ll understand how educational tools can be designed to support learning. You’ll also learn about Racket’s debugging and expansion facilities.
Core Challenges You’ll Face
- Syntax highlighting (coloring different syntax elements) - maps to lexer/tokenizer
- Stepper (showing each reduction step) - maps to annotating code
- Error messages (beginner-friendly) - maps to exception handling
- GUI integration (editor + output) - maps to racket/gui
Key Concepts
- Syntax coloring: DrRacket extension API
- Macro expansion: “The Racket Reference” - Expansion
- DrRacket tools: DrRacket documentation
Real World Outcome
┌──────────────────────────────────────────────────────────────────┐
│ Learning Racket IDE │
├──────────────────────────────────────────────────────────────────┤
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ (define (factorial n) │ │
│ │ (if (= n 0) │ │
│ │ 1 │ │
│ │ (* n (factorial (- n 1))))) │ │
│ │ │ │
│ │ (factorial 5) │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ [Run] [Step] [Clear] │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Step 1: (factorial 5) │ │
│ │ Step 2: (if (= 5 0) 1 (* 5 (factorial (- 5 1)))) │ │
│ │ Step 3: (if #f 1 (* 5 (factorial (- 5 1)))) │ │
│ │ Step 4: (* 5 (factorial 4)) │ │
│ │ ... │ │
│ │ Result: 120 │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
The Core Question You’re Answering
“How can we build tools that make learning programming easier?”
Implementation Hints
Hint 1 (Basic Editor with Syntax Coloring):
Use text% from racket/gui with color-range for highlighting.
Hint 2 (Simple Stepper):
Insert printf calls or use expansion hooks to trace evaluation.
Hint 3 (Error Messages): Wrap evaluation in an exception handler; translate low-level errors to beginner-friendly messages.
Learning Milestones
- Editor accepts code - Basic GUI works
- Syntax colors correctly - Lexer works
- Stepper shows steps - Evaluation tracing works
- Errors are friendly - You improved the learning experience
Project 15: Testing Framework from Scratch
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Python (pytest internals), JavaScript (Jest)
- Difficulty: Level 3: Advanced
- Time Estimate: 1-2 weeks
- Knowledge Area: Testing / Macro Design
- Main Book: RackUnit source code
What You’ll Build
A testing framework with test macros (check-equal?, check-true, check-exn), test suites, setup/teardown, and a test runner with colored output.
Why It Teaches Racket
Testing frameworks are a great example of macros that improve developer experience. Your test assertions will capture the original expressions for better error messages. This shows how macros can record source information.
Core Challenges You’ll Face
- Assertion macros (capturing expressions) - maps to syntax-source
- Test organization (suites, cases) - maps to dynamic binding
- Failure reporting (showing expected vs actual) - maps to error presentation
- Test discovery (finding tests automatically) - maps to module inspection
Key Concepts
- Source locations: “The Racket Reference” - Syntax objects
- RackUnit design: RackUnit documentation
- Test organization patterns: xUnit patterns
Real World Outcome
#lang racket
(require "my-test.rkt")
(define-test-suite math-tests
(test-case "addition"
(check-equal? (+ 1 2) 3)
(check-equal? (+ 0 0) 0))
(test-case "division"
(check-equal? (/ 10 2) 5)
(check-exn exn:fail? (lambda () (/ 1 0)))))
(run-tests math-tests)
Output:
Running math-tests...
addition: PASS (2 checks)
division: PASS (2 checks)
2/2 tests passed
With a failure:
Running math-tests...
addition: FAIL
check-equal? failed
Location: my-tests.rkt:5:4
Expected: 4
Actual: 3
Expression: (check-equal? (+ 1 2) 4)
division: PASS (2 checks)
1/2 tests passed (1 failure)
The Core Question You’re Answering
“How can macros capture source information to produce better error messages?”
Implementation Hints
Hint 1 (Basic Check Macro):
(define-syntax (check-equal? stx)
(syntax-case stx ()
[(_ actual expected)
(with-syntax ([src (syntax-source stx)]
[line (syntax-line stx)]
[col (syntax-column stx)])
#'(let ([a actual] [e expected])
(unless (equal? a e)
(test-fail! 'check-equal?
'src line col
e a
'(check-equal? actual expected)))))]))
Hint 2 (Test Suite Structure):
(struct test-suite (name cases) #:transparent)
(struct test-case (name thunk) #:transparent)
(define-syntax define-test-suite
...)
Learning Milestones
- Basic checks work - You understand assertion macros
- Failures show expressions - You capture source locations
- Suites organize tests - You understand test structure
- Runner produces nice output - You have a complete framework
Project 16: Package Development and Distribution
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Python (PyPI), Node (npm)
- Difficulty: Level 2: Intermediate
- Time Estimate: 1 week
- Knowledge Area: Software Engineering / Packaging
- Main Book: “The Racket Package System”
What You’ll Build
Package one of your earlier projects as a proper Racket package. Create an info.rkt, write Scribble documentation, set up tests, and publish to the Racket package catalog.
Why It Teaches Racket
Understanding the package system is essential for real-world Racket development. You’ll learn about module paths, dependencies, collections, and the documentation system (Scribble).
Core Challenges You’ll Face
- Package structure (files, directories, info.rkt) - maps to package layout
- Dependencies (deps, build-deps) - maps to info.rkt
- Documentation (Scribble) - maps to #lang scribble
- Publishing (raco pkg install –catalog) - maps to package catalog
Key Concepts
- Packages: “The Racket Package System” documentation
- Scribble: “The Scribble Guide”
- info.rkt: Package metadata
Real World Outcome
my-package/
info.rkt
main.rkt
private/
helpers.rkt
scribblings/
my-package.scrbl
tests/
test-main.rkt
; info.rkt
#lang info
(define collection "my-package")
(define version "1.0.0")
(define deps '("base" "rackunit-lib"))
(define build-deps '("scribble-lib" "racket-doc"))
(define scribblings '(("scribblings/my-package.scrbl" ())))
Commands:
# Test locally
raco test my-package/
# Build docs
raco setup --doc-index my-package
# Package
raco pkg create my-package/
# Install locally
raco pkg install my-package/
# Publish (after setup)
raco pkg update --catalog https://pkgs.racket-lang.org \
--name my-package ./my-package.zip
The Core Question You’re Answering
“How do you share Racket code with the community in a structured way?”
Learning Milestones
- Package installs locally - Structure is correct
- Tests run via raco test - Test setup works
- Docs build - Scribble works
- Package published - You contributed to the ecosystem!
Project 17: Creating a Full #lang Language
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: None (Racket-unique feature)
- Difficulty: Level 4: Expert
- Time Estimate: 3-4 weeks
- Knowledge Area: Language Design / Compilers
- Main Book: “Beautiful Racket” by Matthew Butterick
What You’ll Build
A complete programming language with custom syntax, different from S-expressions. For example, a simple imperative language with C-like syntax, or a query language like SQL, or a markup language.
Why It Teaches Racket
This is the pinnacle of Racket’s language-oriented programming. You’ll implement every layer: reader (parsing), expander (macro expansion), and runtime. You’ll see that languages are just libraries.
Core Challenges You’ll Face
- Lexer/tokenizer (breaking input into tokens) - maps to brag or parser-tools
- Parser (building syntax trees) - maps to grammar rules
- Expander (transforming to Racket) - maps to syntax transformers
- Runtime (any needed runtime support) - maps to #%app, #%datum, etc.
Key Concepts
- brag parser generator: “Beautiful Racket”
- Syntax objects: “The Racket Reference”
- #%module-begin: Controls module semantics
Real World Outcome
Example: A simple imperative language
; file: example.funlang
#lang funlang
func factorial(n) {
if n <= 1 {
return 1
} else {
return n * factorial(n - 1)
}
}
print(factorial(5))
Output:
120
The Core Question You’re Answering
“How do you implement a complete programming language as a Racket library?”
Implementation Hints
Hint 1 (Grammar with brag):
; funlang/grammar.rkt
#lang brag
funlang-program : statement*
statement : function-def | expr-statement
function-def : "func" ID "(" params ")" block
block : "{" statement* "}"
...
Hint 2 (Reader):
; funlang/lang/reader.rkt
(define (read-syntax src port)
(define parse-tree (parse src (make-tokenizer port)))
(datum->syntax #f
`(module funlang-mod funlang/expander
,parse-tree)))
Hint 3 (Expander):
; funlang/expander.rkt
(define-syntax-rule (funlang-module-begin tree ...)
(#%module-begin
(compile-program tree ...)))
(define (compile-program tree)
; Transform parse tree to Racket
...)
Learning Milestones
- Lexer tokenizes input - You understand lexical analysis
- Parser builds trees - You understand grammars
- Expander produces Racket - You understand compilation
- Programs run correctly - You’ve created a language!
Project 18: Continuation-Based Backtracking
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Prolog (for concepts)
- Difficulty: Level 4: Expert
- Time Estimate: 2 weeks
- Knowledge Area: Control Flow / Logic Programming
- Main Book: “The Reasoned Schemer”
What You’ll Build
A backtracking search library using continuations. Implement an amb (ambiguous) operator that explores multiple possibilities. Use it to solve puzzles like the N-queens problem or Sudoku.
Why It Teaches Racket
First-class continuations are one of Racket’s most powerful features. This project shows how they enable non-local control flow—specifically, the ability to “go back in time” and try a different choice.
Core Challenges You’ll Face
- call/cc (capturing continuations) - maps to first-class control
- amb implementation (non-deterministic choice) - maps to backtracking
- Failure continuation (what to do when stuck) - maps to continuation management
- Efficient exploration (pruning search space) - maps to constraint propagation
Key Concepts
- Continuations: “SICP” - Section 4.3
- amb: “The Reasoned Schemer”
- Backtracking: Logic programming literature
Real World Outcome
#lang racket
(require "amb.rkt")
; amb chooses non-deterministically
; fail backtracks to previous choice
(define (a-number-between lo hi)
(if (> lo hi)
(fail)
(amb lo (a-number-between (+ lo 1) hi))))
; Find all Pythagorean triples
(define (pythagorean-triples n)
(let ([a (a-number-between 1 n)]
[b (a-number-between 1 n)]
[c (a-number-between 1 n)])
(require (= (+ (* a a) (* b b)) (* c c)))
(require (<= a b))
(list a b c)))
(all-solutions (pythagorean-triples 20))
; => '((3 4 5) (5 12 13) (6 8 10) (8 15 17) (9 12 15) (12 16 20))
; 8-Queens
(define (queens n)
(define (safe? col queens)
; Check if placing a queen at col is safe given previous queens
...)
(define (place-queens row)
(if (> row n)
'()
(let ([col (a-number-between 1 n)])
(require (safe? col (place-queens (- row 1))))
(cons col (place-queens (- row 1))))))
(place-queens n))
(first-solution (queens 8))
; => '(1 5 8 6 3 7 2 4)
The Core Question You’re Answering
“How can continuations implement backtracking search?”
Implementation Hints
Hint 1 (Basic amb with call/cc):
(define fail-stack '())
(define (fail)
(if (empty? fail-stack)
(error "No more choices")
(let ([f (first fail-stack)])
(set! fail-stack (rest fail-stack))
(f))))
(define-syntax amb
(syntax-rules ()
[(_) (fail)]
[(_ x) x]
[(_ x y ...)
(let/cc k
(set! fail-stack
(cons (lambda () (k (amb y ...)))
fail-stack))
x)]))
(define-syntax require
(syntax-rules ()
[(_ pred)
(unless pred (fail))]))
Hint 2 (Collecting All Solutions): Wrap the computation and collect each success, then fail to continue.
Learning Milestones
- amb makes choices - Basic continuation capture works
- fail backtracks - You understand continuation invocation
- Pythagorean triples found - Constraint solving works
- N-queens solved - You’ve built a logic programming engine!
Project 19: Foreign Function Interface (FFI)
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Python (ctypes)
- Difficulty: Level 3: Advanced
- Time Estimate: 1-2 weeks
- Knowledge Area: Systems Programming / Interop
- Main Book: “The Racket Foreign Interface”
What You’ll Build
Interface with C libraries using Racket’s FFI. Wrap a C library (like SQLite, zlib, or a system library) and use it from Racket.
Why It Teaches Racket
Racket can interface with C code, opening the door to thousands of existing libraries. Understanding FFI shows how high-level and low-level code can coexist.
Core Challenges You’ll Face
- Type mappings (Racket types to C types) - maps to ffi-lib, define-ffi-definer
- Memory management (who allocates/frees) - maps to pointers, malloc, free
- Callbacks (C calling Racket) - maps to _fun _callback
- Structs (C struct layout) - maps to define-cstruct
Key Concepts
- FFI basics: “The Racket Foreign Interface”
- C types: C language knowledge
- Memory safety: Understanding allocation
Real World Outcome
#lang racket
(require ffi/unsafe)
; Load SQLite library
(define sqlite-lib (ffi-lib "libsqlite3"))
; Define types
(define-cpointer-type _sqlite3)
(define-cpointer-type _sqlite3_stmt)
; Define functions
(define sqlite3_open
(get-ffi-obj "sqlite3_open" sqlite-lib
(_fun _string (db : (_ptr o _sqlite3)) -> _int)))
(define sqlite3_exec
(get-ffi-obj "sqlite3_exec" sqlite-lib
(_fun _sqlite3 _string _pointer _pointer _pointer -> _int)))
; Use it
(define-values (result db) (sqlite3_open ":memory:"))
(when (= result 0)
(sqlite3_exec db "CREATE TABLE test (id INTEGER, name TEXT)" #f #f #f)
(sqlite3_exec db "INSERT INTO test VALUES (1, 'hello')" #f #f #f)
(displayln "Database operations completed"))
The Core Question You’re Answering
“How do you call C code from Racket?”
Implementation Hints
Hint 1 (Basic FFI):
(require ffi/unsafe)
(define libc (ffi-lib #f)) ; System C library
(define strlen
(get-ffi-obj "strlen" libc (_fun _string -> _size)))
(strlen "hello") ; => 5
Hint 2 (Defining C Structs):
(define-cstruct _point
([x _int]
[y _int]))
(define p (make-point 10 20))
(point-x p) ; => 10
Learning Milestones
- Simple function calls work - Basic FFI works
- Strings pass correctly - Type mapping works
- Structs work - Memory layout understood
- Callbacks work - Bidirectional interop works
Project 20: Racket Compiler or Optimizer
- File: RACKET_PROGRAMMING_MASTERY.md
- Main Programming Language: Racket
- Alternative Programming Languages: Any (general compiler knowledge)
- Difficulty: Level 5: Master
- Time Estimate: 4+ weeks
- Knowledge Area: Compilers / Optimization
- Main Book: “Essentials of Compilation” (uses Racket!)
What You’ll Build
A compiler for a subset of Racket (or your own language from Project 17) that targets x86-64 assembly or LLVM IR. Alternatively, build an optimizer that performs transformations like constant folding, dead code elimination, and inlining.
Why It Teaches Racket
This is the ultimate test of understanding. You’ll see how high-level code becomes machine code. Racket is an excellent language for building compilers due to its macro system and pattern matching.
Core Challenges You’ll Face
- IR design (intermediate representation) - maps to data structure design
- Code generation (producing assembly/LLVM) - maps to target architecture
- Optimization passes (transforming IR) - maps to program analysis
- Runtime support (GC, primitives) - maps to systems programming
Key Concepts
- Compilation phases: “Essentials of Compilation”
- x86-64 assembly: Architecture references
- LLVM: LLVM documentation
- Optimization: “Engineering a Compiler” by Cooper & Torczon
Real World Outcome
Input:
(let ([x 5]
[y 10])
(+ (* x x) y))
After parsing:
(Let ([x (Int 5)] [y (Int 10)])
(Prim '+ (Prim '* (Var 'x) (Var 'x)) (Var 'y)))
After optimization (constant folding):
(Int 35)
Generated x86-64:
.globl main
main:
movq $35, %rax
ret
Output when run:
35
The Core Question You’re Answering
“How is high-level code transformed into executable machine code?”
Implementation Hints
Hint 1 (AST Definition):
(struct Int (value) #:transparent)
(struct Var (name) #:transparent)
(struct Prim (op args) #:transparent)
(struct Let (bindings body) #:transparent)
Hint 2 (Constant Folding Pass):
(define (fold-constants expr)
(match expr
[(Prim '+ (list (Int a) (Int b))) (Int (+ a b))]
[(Prim '* (list (Int a) (Int b))) (Int (* a b))]
[(Let bindings body)
(Let (map (lambda (b) (list (first b) (fold-constants (second b)))) bindings)
(fold-constants body))]
[_ expr]))
Hint 3 (Code Generation): Target a simple subset first. Handle integers, then variables, then primitives, then control flow.
Learning Milestones
- Parser works - You can read source code
- Optimizer transforms code - Analysis and transformation work
- Code generator produces assembly - You target real hardware
- Programs run correctly - End-to-end compilation works
Project Comparison Table
| Project | Difficulty | Time | Focus Area | Key Skill Learned |
|---|---|---|---|---|
| 1. REPL Calculator | Beginner | Weekend | Syntax | S-expressions, prefix notation |
| 2. Environment Evaluator | Intermediate | 1 week | Variables | Scope and environments |
| 3. Higher-Order Functions | Intermediate | 1 week | FP | Functions as values |
| 4. Trees and Graphs | Intermediate | 1-2 weeks | Data | Recursive data structures |
| 5. Expression Transformer | Intermediate | 1 week | Matching | Pattern matching |
| 6. Contracts Library | Intermediate | 1 week | Contracts | Runtime verification |
| 7. Typed Calculator | Advanced | 1 week | Types | Gradual typing |
| 8. GUI Application | Intermediate | 1-2 weeks | GUI | Event-driven programming |
| 9. Web Application | Advanced | 2 weeks | Web | Continuations |
| 10. First Macros | Advanced | 1-2 weeks | Macros | Code transformation |
| 11. Parser Combinators | Advanced | 2 weeks | Parsing | Functional patterns |
| 12. 2D Game | Intermediate | 2-3 weeks | Games | State machines |
| 13. Config DSL | Advanced | 2 weeks | #lang | Language creation |
| 14. Educational IDE | Advanced | 2-3 weeks | Tools | IDE design |
| 15. Testing Framework | Advanced | 1-2 weeks | Testing | Source locations |
| 16. Package Development | Intermediate | 1 week | Packaging | Community contribution |
| 17. Full #lang | Expert | 3-4 weeks | Language | Complete language |
| 18. Backtracking | Expert | 2 weeks | Continuations | Control flow |
| 19. FFI | Advanced | 1-2 weeks | Interop | C interfacing |
| 20. Compiler/Optimizer | Master | 4+ weeks | Compilers | Code generation |
Recommended Learning Paths
Path 1: Pure Functional Foundation (8-10 weeks)
For those wanting to master functional programming concepts:
- Project 1: REPL Calculator (understand syntax)
- Project 2: Environment Evaluator (understand scope)
- Project 3: Higher-Order Functions (understand FP core)
- Project 4: Trees and Graphs (understand recursive data)
- Project 5: Expression Transformer (understand pattern matching)
- Project 11: Parser Combinators (understand composition)
Path 2: Language Designer Track (12-16 weeks)
For those interested in creating their own languages:
- Project 1: REPL Calculator
- Project 2: Environment Evaluator
- Project 10: First Macros (understand code transformation)
- Project 13: Config DSL (first #lang)
- Project 17: Full #lang (complete language)
- Project 20: Compiler/Optimizer (target real hardware)
Path 3: Application Developer Track (10-12 weeks)
For those wanting to build real applications:
- Project 1: REPL Calculator
- Project 3: Higher-Order Functions
- Project 6: Contracts Library
- Project 8: GUI Application
- Project 9: Web Application
- Project 16: Package Development
Path 4: Education/Tools Track (12-14 weeks)
For those interested in educational tools:
- Project 1: REPL Calculator
- Project 2: Environment Evaluator
- Project 5: Expression Transformer
- Project 14: Educational IDE
- Project 15: Testing Framework
- Project 16: Package Development
Interview Questions from These Projects
Core Racket Questions
- What makes Racket’s #lang feature unique? Give an example of how it’s used.
- Explain the difference between functions and macros. When would you use each?
- What is hygienic macro expansion? Why does it matter?
- How do Racket contracts differ from types? What are the tradeoffs?
Language Implementation Questions
- Walk me through how you would implement a simple interpreter.
- What are the phases of compilation? How does Racket’s macro system fit in?
- Explain how environments work in a language with lexical scoping.
Functional Programming Questions
- Implement
fold-leftandfold-right. What’s the difference? - How would you implement a parser using parser combinators?
- What are continuations? Give an example of their use.
Practical Questions
- How would you structure a Racket package for distribution?
- Explain how you would debug a macro that isn’t expanding correctly.
- How would you interface with a C library from Racket?
Books and Resources
Primary Books
| Book | Author | Best For |
|---|---|---|
| “How to Design Programs” (HtDP) | Felleisen et al. | Beginners, design methodology |
| “The Racket Guide” | Racket team | Comprehensive reference |
| “Realm of Racket” | Various | Games, fun introduction |
| “Beautiful Racket” | Matthew Butterick | Creating languages |
| “Fear of Macros” | Greg Hendershott | Understanding macros |
| “Essentials of Compilation” | Jeremy Siek et al. | Compiler construction |
| “The Reasoned Schemer” | Friedman et al. | Logic programming |
Online Resources
- The Racket Guide
- The Racket Reference
- Racket School
- How to Design Programs
- Beautiful Racket
- Fear of Macros
Community
Summary
Racket represents a unique philosophy in programming language design: languages are libraries. By working through these projects, you’ll learn not just how to program in Racket, but how to think about programming languages themselves.
You’ll start with the basics—understanding S-expressions and recursive evaluation—and progress to creating your own languages with custom syntax. Along the way, you’ll master macros, understand continuations, build GUIs and web applications, and even write a compiler.
The skills you gain transfer everywhere. Understanding how languages work makes you a better programmer in any language. The functional programming patterns you learn apply to JavaScript, Python, Rust, and beyond. The macro design principles influence how you think about abstraction and code generation.
Most importantly, Racket teaches you that the boundary between “using a language” and “creating a language” is an illusion. Languages are just tools, and you can build the tools you need.
Now go build something—and if the language doesn’t fit your problem, change the language.
This guide covers 20 projects designed to take you from Racket beginner to language designer. Each project builds on previous knowledge, creating a coherent learning path through one of the most powerful programming environments ever created.