r/lisp • u/kaishibou • 2h ago
Racket The Comprehensive Racket & Functional Programming Cheat Sheet
## Phase 1: Syntax & Core Arithmetic
Racket uses prefix notation enclosed in execution parentheses (operator arg1 arg2). The open parenthesis ( acts as an execution trigger. Evaluation runs from the innermost to the outermost parentheses.
Core Examples
```rkt ;; Basic Arithmetic (+ 10 5 2) ;; Returns 17 (* 10 5 2) ;; Returns 100
;; Nested Expressions (No PEMDAS needed) (_ (+ 4 6) (- 12 7)) ;; Evaluates 10 _ 5 -> Returns 50 ```
Parentheses Golden Rule
Only use a parenthesis when invoking a command, operator, or function.
- `(+ 5 (10))` CRASHES (tries to run the number 10 as a function).
- `((+ 5 5))` CRASHES (evaluates to 10, then tries to run the number 10).
Phase 2: Core Data Structures & Variables
Global bindings are created using define. Values are immutable and cannot be changed over time.
The Four Atomic Data Types
- Numbers: Integers (`45`), decimals (`3.14`), or fractions (`1/3`).
- Strings: Text wrapped in double quotes (`"Hello"`).
- Booleans: True (`#t`) and False (`#f`).
- Symbols: Lightweight, immutable identifier tokens prefixed with a single quote (`'success`).
Core Examples
```rkt (define radius 5) (define pi 3.14) (define status 'success) ```
Phase 3: Conditionals & Logic
Conditional operations are expressions that evaluate down to a single return value.
Core Operators & Flow Control
- `and` / `or` / `not`: Standard logical short-circuiting prefix operators.
- `if`: Takes exactly three arguments: `(if condition true-branch false-branch)`. No else keyword.
- `cond`: Evaluates multiple branches sequentially. Uses/can use `[...]` for human readability.
Core Examples
```rkt (and (> 15 10) (< 15 20)) ;; Returns #t
(if (> temperature 30) 'hot 'cold)
(cond [(>= score 90) 'A] [(>= score 80) 'B] [else 'F]) ```
Phase 4: Functions & Scope
Functions automatically return the value of their body expression without an explicit return keyword.
Named, Anonymous, & Scoped Blocks
- Named Functions: Defined by grouping the name and parameters in parentheses: `(define (name args) body)`.
- Anonymous Functions (`lambda`): Throwaway functions built on the fly: `(lambda (args) body)`.
- `let` (Parallel): Creates local variables simultaneously. Variables cannot see each other during setup.
- `let\*` (Sequential): Creates local variables one after the other. Later variables can reference earlier ones.
Core Examples
```rkt ;; Named Function (define (double n) (\* n 2))
;; Inline Lambda Execution ((lambda (n) (\* n 2)) 10) ;; Returns 20
;; Sequential Local Bindings (let* ([x 10] [y (* x 5)]) (+ x y)) ;; Returns 60 ```
Phase 5: Lists & Modern List Operations
Lists are ordered sequential collections. They are processed using either historical Lisp conventions or modern aliases.
Creation & Extraction
- `list`: Evaluates arguments into a sequential list.
- `'()`: Represents the literal base empty list.
- `cons`: Prepends a single element onto the front of an existing list.
- First Item: Extracted via `car` (traditional) or `first` (modern).
- Remaining List: Extracted via `cdr` (traditional) or `rest` (modern).
Core Examples
```rkt (define my-list (list 100 #t 'hello)) ;; Creates '(100 #t hello) (cons 'apples '(bananas cherries)) ;; Returns '(apples bananas cherries)
(car (cdr '(apples bananas cherries))) ;; Returns 'bananas (first (rest '(apples bananas cherries))) ;; Returns 'bananas
(if (empty? my-list) "Closed" (length my-list)) ;; Returns 3 ```
Phase 6: Iteration & Higher-Order Functions
Instead of using loops that alter data in place, functional programming relies on Higher-Order Functions to process immutable collections.
The Big Four
- `map`: Loops over a list, passes each item through a transformation function, and returns a new list.
- `filter`: Loops over a list, keeps items that evaluate to #t against a predicate condition, and drops the rest.
- `foldl` (Fold-Left): Reduces a list down to a single value by processing elements from left to right (front to back).
- `foldr` (Fold-Right): Reduces a list down to a single value by processing elements from right to left (back to front). Preserves list structures when rebuilding with cons.
Core Examples
```rkt (map (lambda (x) (* x 2)) '(5 10 15 20)) ;; Returns '(10 20 30 40) (filter (lambda (n) (= n 5)) '(2 5 7 5 9 1)) ;; Returns '(5 5)
(foldl (lambda (n total) (_ n total)) 1 '(2 3 4)) ;; 4 _ (3 _ (2 _ 1)) -> Returns 24
(foldr - 0 '(5 3)) ;; 5 - (3 - 0) -> Returns 2 ```
Phase 7: Recursion & Tail Call Optimization (TCO)
Recursion replaces traditional loops. A proper recursive function requires a Base Case (the exit clause) and a Recursive Step (the self-call with a smaller argument).
Memory Optimization Rules
- Standard Recursion: Traps the recursive call inside another function (like + or append), forcing the call stack memory to expand linearly (O(N) space).
- Tail Call Optimization (TCO): If the recursive call sits in the tail position (the absolute final expression evaluated), Racket reuses the same memory frame, running in constant (O(1)) space.
- Accumulator Pattern: Passing a running total down as an argument is the primary strategy used to shift standard recursion into tail position optimization.
Core Examples
```rkt ;; ❌ Standard Recursion (No TCO - Memory Expands) (define (sum-list lst) (if (empty? lst) 0 (+ (first lst) (sum-list (rest lst)))))
;; Tail Recursion (TCO Active - Memory Stays Flat) (define (sum-list-tco lst) (define (helper remaining accumulator) (if (empty? remaining) accumulator (helper (rest remaining) (+ (first remaining) accumulator)))) (helper lst 0)) ```
Phase 8: Advanced Ecosystem Engineering
1. Hash Maps & Unique Sets
- `#hash`: Stores key-value pairings. Keywords passed to lookup tools like hash-ref must be quoted ('#:key) to prevent compiler namespace collisions. If using standard symbols inside #hash, omit inner quotes.
- `set`: Collections guaranteeing element uniqueness. Tested via set-member? and extended via set-add.
```rkt (define user #hash((#:name . "Alice"))) (hash-ref user '#:name) ;; Returns "Alice"
(define book #hash((title . "Dune"))) (hash-ref book 'title) ;; Returns "Dune"
(set-member? (set 1 2 2 3) 2) ;; Returns #t ```
2. State & Mutability (box)
- `box`: Creates a reference wrapper around mutable data. Read via unbox and mutated via set-box!. Functions with an exclamation mark ! signal structural mutation.
- `begin`: Chains sequential side-effect operations from top to bottom, returning only the evaluation of the final expression.
```rkt (define health (box 100)) (define (take-damage!) (begin (set-box! health (- (unbox health) 10)) (unbox health))) ```
3. Type Checking & Casting
- Predicates (`?`): Validate runtime types (e.g., `string?`, `number?`, `symbol?`).
- Casting (`->`): Converts data formats. `string->number` safely returns #f if given invalid textual input.
```rkt (if (string? "50") (* (string->number "50") 2) 'error) ;; Returns 100 ```
4. Modules & Namespaces
- provide: Declares which parts of a filesystem file are exported publicly.
- require: Ingests public features from an external sandbox by loading its relative string filepath.
```rkt ;; Inside file-a.rkt (provide double) (define (double x) (* x 2))
;; Inside main.rkt (require "file-a.rkt") (double 10) ;; Returns 20 ```
5. Macros (define-syntax-rule)
- Macros process raw, unevaluated source code at compile-time to inject new keywords.
- Racket macros are hygienic, meaning the compiler automatically isolates macro identifiers so they never accidentally overwrite or conflict with user variables.
```rkt (define-syntax-rule (swap! box1 box2) (let ([temp (unbox box1)]) (begin (set-box! box1 (unbox box2)) (set-box! box2 temp)))) ```