Racket: A Language for Creating Languages
Racket is a general-purpose programming language and a platform for creating programming languages. It belongs to the Lisp/Scheme family and is used in education, research, and industry for building domain-specific languages (DSLs).
In this tutorial, you’ll learn Racket’s core concepts: its role as a language workbench, DrRacket IDE, parentheses syntax, macros for metaprogramming, contracts for runtime verification, and the teaching language suite.
What You’ll Learn
- Racket as a language workbench (PL ideas)
- DrRacket IDE features
- Parentheses syntax and prefix notation
- Macros — programs that write programs
- Contracts for runtime assertions
- Teaching languages (Beginning Student, Intermediate, Advanced)
- Building a simple DSL
Why Racket Matters
Racket is used to create programming languages. The Pyret language, the Scribble documentation system, and the HtDP (How to Design Programs) curriculum all use Racket. At DodaTech, the macro system in Racket inspires metaprogramming patterns in DodaZIP’s template engine.
Learning Path
flowchart LR
A[Lisp Concepts] --> B[Racket Basics<br/>You are here]
B --> C[Macros & Metaprogramming]
C --> D[DSL Creation]
B --> E[Contracts & Testing]
style B fill:#f90,color:#fff
Parentheses and Prefix Notation
Racket uses prefix notation: the operator comes first, followed by arguments:
#lang racket
; Prefix notation
(+ 1 2 3) ; 6
(* 2 3 4) ; 24
(string-append "Hello" " " "World") ; "Hello World"
; Nested expressions
(define (square x) (* x x))
(square 5) ; 25
; Conditionals
(define (abs-val x)
(if (negative? x)
(- x)
x))
(abs-val -10) ; 10
(abs-val 5) ; 5
; List operations
(define numbers '(1 2 3 4 5))
(map sqr numbers) ; '(1 4 9 16 25)
(filter odd? numbers) ; '(1 3 5)
(foldl + 0 numbers) ; 15Expected output: The REPL evaluates each expression and prints the result. Note that #lang racket tells Racket which language to use — you can swap it for any language you define.
DrRacket IDE
DrRacket is the official IDE with unique features:
| Feature | Purpose |
|---|---|
| Stepper | Step through evaluation one reduction at a time |
| Syntax coloring | Parentheses matching with rainbow colors |
| REPL | Interactive evaluation with history |
| Require | Easy module importing |
| Language selector | Switch between student and professional languages |
The stepper is invaluable for beginners — it shows exactly how expressions evaluate, revealing the substitution model.
Data Structures
Racket’s primary data structures:
#lang racket
; Lists (linked)
(define fruits '(apple banana cherry))
(first fruits) ; 'apple
(rest fruits) ; '(banana cherry)
(cons 'date fruits) ; '(date apple banana cherry)
; Vectors (fixed-size, O(1) access)
(define vec #(10 20 30))
(vector-ref vec 1) ; 20
(vector-set! vec 1 25)
vec ; #(10 25 30)
; Hash tables
(define scores (make-hash))
(hash-set! scores 'alice 95)
(hash-set! scores 'bob 87)
(hash-ref scores 'alice) ; 95
; Structures (like classes)
(struct point (x y) #:transparent)
(define p (point 3 4))
(point-x p) ; 3
(point-y p) ; 4
; Pattern matching
(match p
[(point 0 0) "origin"]
[(point x y) (format "(~a, ~a)" x y)])
; "(3, 4)"Expected output: Each form evaluates to its result. Structures with #:transparent print their fields.
Macros — Programs That Write Programs
Macros transform code before evaluation. They’re Racket’s most powerful feature:
#lang racket
; Simple macro — infix notation
(require syntax/parse/define)
(define-syntax-rule (infix a op b)
(op a b))
(infix 3 + 5) ; 8
(infix 10 * 2) ; 20
; More complex macro — conditional logging
(define-syntax (log-if-debug stx)
(syntax-parse stx
[(_ expr)
#'(when *debug-mode*
(printf "DEBUG: ~a => ~a~n" 'expr expr)
expr)]))
(define *debug-mode* #t)
(log-if-debug (+ 1 2))
; DEBUG: (+ 1 2) => 3
; 3Expected behavior: The log-if-debug macro expands to a when form that conditionally prints the expression and its value. In non-debug mode, the logging is removed entirely — zero runtime cost.
Contracts
Contracts are runtime assertions attached to functions via provide:
#lang racket
(provide
(contract-out
[divide (->i ([a number?] [b (and/c number? (not/c zero?))])
[result number?])]))
(define (divide a b)
(/ a b))
; Usage — contract violation
; (divide 10 0)
; => divide: contract violation
; expected: (and/c number? (not/c zero?))
; given: 0Contracts document and enforce function behavior at module boundaries — like types for untyped code.
Teaching Languages
Racket’s teaching languages scaffold learning:
| Language | Features | For |
|---|---|---|
| Beginning Student | Functions, numbers, strings, images | Absolute beginners |
| Intermediate | Lists, lambda, local definitions | Learning recursion |
| Advanced Student | Mutation, vectors, structs | Transition to professional |
#lang beginner
; No mutation, no loops, no variables
; Everything is function application
(define (factorial n)
(cond
[(zero? n) 1]
[else (* n (factorial (- n 1)))]))
(factorial 5) ; 120Common Mistakes
1. Forgetting #lang
Every Racket file must start with #lang racket (or another language). Without it, the reader doesn’t know what language to use.
2. Mismatched Parentheses
Racket highlights matching parentheses. Count them carefully. Use DrRacket’s auto-indent (Tab) to check nesting.
3. Confusing quote and quasiquote
'(1 2 3) is a literal list. `(1 ,(+ 1 2) 3) is quasiquote — the , unquotes for evaluation. Result: '(1 3 3).
4. Using set! Without Understanding Mutation
Mutation is available but discouraged. Prefer functional updates. set! changes a variable; set-car! changes a list cell.
5. Forgetting That Everything Is an Expression
if, cond, begin, let — all produce values. There’s no statement/expression distinction.
6. Not Using the Stepper
If your code doesn’t do what you expect, run the stepper. It shows every step of evaluation.
Practice Questions
1. What does #lang racket mean?
It tells Racket which language to use. Racket is a language workbench — you can use or define any language.
2. What is a macro?
A macro is a compile-time code transformation. Macros let you extend the language syntax and implement domain-specific languages.
3. What is a contract?
A runtime assertion attached via provide. Contracts enforce behavior at module boundaries, catching violations at the source.
4. What is the difference between car/cdr and first/rest?
They’re aliases. car/cdr are historical (from Lisp). first/rest are modern Racket. Use first/rest for readability.
5. Challenge: Write a macro that defines a simple loop.
Create a repeat macro: (repeat n body ...) that evaluates body n times.
Mini Project: Infix Calculator DSL
#lang racket
(define-syntax-rule (calc expr ...)
(calc-helper 'expr expr) ...)
(define (calc-helper original result)
(printf "~a = ~a~n" original result))
(calc
1 + 2
10 * 5
(+ 1 (* 2 3)))
; 1 + 2 = 3
; 10 * 5 = 50
; (+ 1 (* 2 3)) = 7FAQ
What’s Next
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro