Getting Started with Racket
Install DrRacket, get past the parentheses in ten minutes, and learn the substitution model that makes functional code predictable.
Racket looks alien for about ten minutes. Then something clicks, and the way you read every language changes.
There is a reason it is used in first-year CS courses worldwide: the syntax is so small you can learn all of it in an afternoon, which leaves the whole term for the ideas.
#Install DrRacket
Download from download.racket-lang.org and run the installer. It ships with DrRacket, a complete IDE built for learning.
DrRacket has two panes:
- The definitions window (top) — your program. Click Run to load it.
- The interactions window (bottom) — a REPL where you can call anything you just defined.
That split is genuinely well designed for learning: define in the top, experiment in the bottom, no file juggling.
#The language line
Every Racket file starts by declaring its language:
#lang racketThat is not boilerplate — Racket is a language for making languages, and the #lang line picks which one this file is written in. University courses often use the teaching languages (#lang htdp/bsl), which deliberately restrict features so error messages stay comprehensible for beginners.
Use #lang racket for everything here.
#Everything is a parenthesised list
Here is the entire syntax of Racket:
(function argument1 argument2 ...)That is it. Every single expression follows that shape.
(+ 1 2) ; 3
(* 3 4) ; 12
(- 10 3 2) ; 5 — subtracts both, left to right
(+ 1 2 3 4 5) ; 15 — as many arguments as you like
(max 4 8 15 16) ; 16
(string-append "Coding " "with " "Dom") ; "Coding with Dom"This is prefix notation — the operator comes first. It looks backwards for exactly as long as it takes to notice that it removes every ambiguity you have ever dealt with.
Consider 2 + 3 * 4. To know that this is 14 rather than 20, you must have memorised operator precedence. In Racket:
(+ 2 (* 3 4)) ; 14
(* (+ 2 3) 4) ; 20No precedence rules, no PEMDAS, no ambiguity. The parentheses are the structure. There is nothing to memorise because nothing is implicit.
Also: DrRacket highlights matching parens and auto-indents on Tab. Press Tab constantly and let the editor manage the brackets. Nobody counts parentheses by hand.
#define — name things
#lang racket
(define channel-name "Coding with Dom")
(define subscriber-count 119)
(define pi 3.14159)
(displayln channel-name)Same form for functions:
(define (square n)
(* n n))
(define (area-of-circle radius)
(* pi (square radius)))
(area-of-circle 5) ; 78.53975Read (define (square n) ...) as: define a thing which, when called as (square n), evaluates to this body. The pattern in the definition mirrors the call exactly.
Racket uses kebab-case — area-of-circle, not areaOfCircle. Hyphens are legal in names because there is no infix minus operator to confuse them with.
#Conditionals
(define (classify n)
(cond
[(< n 0) "negative"]
[(= n 0) "zero"]
[(< n 10) "small"]
[else "large"]))
(classify -5) ; "negative"
(classify 42) ; "large"cond tests each condition in order and returns the value of the first matching branch. Square brackets are interchangeable with parentheses in Racket — convention uses them for cond clauses purely for readability.
For a single either/or, if takes exactly three parts:
(if (> score 50) "pass" "fail")Note that if is an expression — it returns a value, it does not "do" something. This is the functional mindset: you are composing values, not issuing commands. There is no else clause to forget because the third argument is mandatory.
#Lists
Lists are the fundamental data structure.
(define tracks (list "python" "php" "ruby" "racket"))
(define nums '(4 8 15 16 23 42)) ; quote is shorthand for list
(first tracks) ; "python"
(rest tracks) ; '("php" "ruby" "racket")
(length nums) ; 6
(list-ref nums 2) ; 15
(reverse nums) ; '(42 23 16 15 8 4)
(cons "scratch" tracks) ; adds to the front, returns a NEW listfirst and rest are the pair that recursion is built on: a list is an item, followed by a smaller list. That definition is recursive, which is why processing lists recursively feels natural here.
#Recursion, and the substitution model
Racket has loops, but recursion is the natural way to express most things.
(define (sum-list lst)
(if (empty? lst)
0
(+ (first lst) (sum-list (rest lst)))))
(sum-list '(1 2 3 4 5)) ; 15Two parts, always: the base case (empty? → 0) and the recursive case that shrinks the problem (rest is always one shorter).
Now here is why Racket is used for teaching. You can trace it purely by substitution:
(sum-list '(1 2 3))
= (+ 1 (sum-list '(2 3)))
= (+ 1 (+ 2 (sum-list '(3))))
= (+ 1 (+ 2 (+ 3 (sum-list '()))))
= (+ 1 (+ 2 (+ 3 0)))
= (+ 1 (+ 2 3))
= (+ 1 5)
= 6Every step is "replace an expression with its value." No mutable variables, no hidden state, nothing off-screen that could change the answer. What you see is genuinely all there is.
That property has a name — referential transparency — and it is the practical argument for functional programming. A function that depends only on its arguments can be tested in isolation, reasoned about locally, and safely run in parallel.
#Higher-order functions
(map square '(1 2 3 4)) ; '(1 4 9 16)
(filter even? '(1 2 3 4 5 6)) ; '(2 4 6)
(foldr + 0 '(1 2 3 4 5)) ; 15
(apply max '(4 8 15 16 23 42)) ; 42
;; lambda for a one-off, unnamed function
(map (lambda (n) (* n 10)) '(1 2 3)) ; '(10 20 30)
(filter (lambda (s) (> (string-length s) 3)) tracks)If you have met map and filter in Python, JavaScript or Ruby, this is where they came from. Lisp had them in 1958.
#Put it together
#lang racket
(define catalogue
(list
(list "Intro to Python" "python" 647 197)
(list "Python Basics" "python" 898 191)
(list "Racket Install" "racket" 234 696)
(list "PHP Web Form" "php" 266 259)))
;; Accessors — name the positions so the rest of the code reads clearly
(define (video-title v) (list-ref v 0))
(define (video-track v) (list-ref v 1))
(define (video-seconds v) (list-ref v 2))
(define (video-views v) (list-ref v 3))
(define (format-duration secs)
(define minutes (quotient secs 60))
(define seconds (remainder secs 60))
(format "~a:~a" minutes (~r seconds #:min-width 2 #:pad-string "0")))
(define (videos-in track)
(filter (lambda (v) (string=? (video-track v) track)) catalogue))
(define (total-seconds videos)
(foldr + 0 (map video-seconds videos)))
(define (describe track)
(define videos (videos-in track))
(printf "~a — ~a lessons, ~a\n"
(string-upcase track)
(length videos)
(format-duration (total-seconds videos)))
(for ([v videos])
(printf " ~a (~a)\n" (video-title v) (format-duration (video-seconds v)))))
(for ([track '("python" "racket" "php")])
(describe track)
(newline))
(printf "Most watched: ~a\n"
(video-title (argmax video-views catalogue)))PYTHON — 2 lessons, 25:45
Intro to Python (10:47)
Python Basics (14:58)
RACKET — 1 lessons, 3:54
Racket Install (3:54)
PHP — 1 lessons, 4:26
PHP Web Form (4:26)
Most watched: Racket InstallA few things introduced there: ~a is the placeholder in printf and format. quotient and remainder are integer division and modulo. argmax returns the element that maximises a function — no sorting required. And for does exist for genuine side effects like printing, which is exactly the right place to use it.
Notice the accessor functions at the top. Rather than scattering (list-ref v 2) through the code, name each position once. This is a small habit with large payoff — when the data shape changes, you edit one line.
Check yourself
4 questions on what you just read. No score is kept — it is only here to catch the bits that did not stick.
-
1What does prefix notation remove entirely?
Show the answer
A. Operator precedence — the parentheses are the structure.
(+ 2 (* 3 4))is 14 and(* (+ 2 3) 4)is 20. Nothing is implicit, so there is nothing to memorise. -
2Why does
'(4 8 15)need the quote?Show the answer
A. Without it, Racket tries to call
4as a function with arguments8and15.Code and data share the same shape in Lisp-family languages, so you need a way to say which one you mean.
-
3What makes the substitution model work as a complete mental model?
Show the answer
A. Functions depend only on their arguments, so every step is "replace an expression with its value".
No mutable state, nothing off-screen that could change the answer. That property — referential transparency — is the practical argument for functional programming.
-
4Why does recursion feel natural for Racket lists?
Show the answer
A. A list is an item followed by a smaller list, which is already a recursive definition.
firstandresthand you exactly those two pieces, which is whyempty?plusrestis the shape of nearly every list function.
#Key takeaways
(function arg1 arg2)is the entire syntax. Prefix notation removes every precedence rule.- Read nested expressions inside out — that is the order Racket evaluates them.
definenames both values and functions; the definition mirrors the call.- A list is an item plus a smaller list, which is why recursion fits so naturally.
- Recursion needs a base case and a shrinking problem, every time.
- Substitution is a complete mental model here: replace each expression with its value.
Finished this one?
The challenge above has no posted solution — that is deliberate. Get it working, then come back for the next lesson.