AI agent skill files related to programming with Racket

Add a skill for general Racket programming

+665
+665
racket.md
··· 1 + --- 2 + name: racket 3 + description: "Use this skill whenever writing, reading, editing, reviewing, or debugging Racket code (.rkt files). Triggers include: any mention of 'Racket', '#lang racket', '.rkt', 'raco', 'syntax-parse', 'DrRacket', or requests involving s-expression-based Lisp/Scheme dialects in a Racket context. Also use when working with Racket macros, Racket package management, Typed Racket, Scribble documentation, or any Racket-ecosystem tool (rackunit, rebellion, resyntax, etc). Use this skill even for tasks that look like 'generic Lisp' if the user's project context is Racket. Do NOT use for Common Lisp, Clojure, Emacs Lisp, or Scheme implementations other than Racket." 4 + --- 5 + 6 + # Racket Programming 7 + 8 + ## Overview 9 + 10 + Racket is a language-oriented programming language descended from Scheme. It is NOT simply "another Scheme" — Racket has its own idioms, standard library, macro system, module system, and conventions that differ significantly from R5RS/R6RS/R7RS Scheme and from Common Lisp. Code that looks like "generic Scheme" is usually not idiomatic Racket. Racket developers often call themselves "Racketeers" (in the Scheme tradition of naming things after illicit activities). 11 + 12 + This skill covers idiomatic Racket style, common pitfalls that AI coding agents make, project structure, testing, macros, and documentation. 13 + 14 + **Reference**: The official Racket style guide is at https://docs.racket-lang.org/style/index.html — consult it when uncertain. 15 + 16 + --- 17 + 18 + ## Critical: Things AI Agents Get Wrong 19 + 20 + These are the most common mistakes. Internalize these before writing any Racket code. 21 + 22 + ### 1. Do NOT write Scheme — write Racket 23 + 24 + ```racket 25 + ;; WRONG — this is Scheme, not Racket 26 + (define (map-filter f pred lst) 27 + (cond 28 + [(null? lst) '()] 29 + [(pred (car lst)) 30 + (cons (f (car lst)) (map-filter f pred (cdr lst)))] 31 + [else (map-filter f pred (cdr lst))])) 32 + 33 + ;; RIGHT — idiomatic Racket uses for loops, match, and the standard library 34 + (define (map-filter f pred lst) 35 + (for/list ([x (in-list lst)] 36 + #:when (pred x)) 37 + (f x))) 38 + ``` 39 + 40 + Key differences from Scheme: 41 + - Use `#lang racket` (not `#lang scheme` or `#lang r5rs`) 42 + - Use square brackets `[]` for cond clauses, let bindings, and for clauses 43 + - Use `for/list`, `for/fold`, `for/hash`, etc. instead of manual recursion over lists 44 + - Use `match` and `match-define` for destructuring, not `car`/`cdr` chains 45 + - Use `first`, `rest`, `empty?` over `car`, `cdr`, `null?` (though the latter work) 46 + - Use `string-append`, `string-join`, etc. — not `display` with `string-port` 47 + - Use `hash` tables, not association lists, for key-value data 48 + - Use `struct` (not `define-record-type` or manual constructors) 49 + - Use `define/contract` or `->` contracts, not ad-hoc assertions 50 + 51 + ### 2. Square brackets are conventional, not optional 52 + 53 + Racket uses `[]` by convention in specific positions. This is one of the most visible markers of idiomatic vs non-idiomatic code. 54 + 55 + ```racket 56 + ;; WRONG — all parens 57 + (cond 58 + ((empty? lst) 0) 59 + ((> (first lst) 10) (add1 (count-big (rest lst)))) 60 + (else (count-big (rest lst)))) 61 + 62 + ;; RIGHT — square brackets for cond clauses 63 + (cond 64 + [(empty? lst) 0] 65 + [(> (first lst) 10) (add1 (count-big (rest lst)))] 66 + [else (count-big (rest lst))]) 67 + ``` 68 + 69 + Use `[]` in these positions: 70 + - `cond` clauses: `[test expr ...]` 71 + - `match` clauses: `[(pattern) expr ...]` 72 + - `let`/`let*`/`letrec` bindings: `(let ([x 1] [y 2]) ...)` 73 + - `for` clauses: `(for/list ([x (in-list xs)]) ...)` 74 + - `case` clauses: `[(datum ...) expr ...]` 75 + - `with-handlers` clauses: `([exn:fail? handler])` 76 + - `syntax-parse` clauses and attribute bindings 77 + - `parameterize` bindings: `(parameterize ([param val]) ...)` 78 + 79 + Do NOT use `[]` for: 80 + - Function application: use `(f x)` not `[f x]` 81 + - Function definitions: use `(define (f x) ...)` not `(define [f x] ...)` 82 + - The outermost form of any expression 83 + 84 + ### 3. Prefer `define` over `let` — reduce indentation 85 + 86 + ```racket 87 + ;; WRONG — unnecessary nesting 88 + (define (process data) 89 + (let* ([cleaned (clean data)] 90 + [parsed (parse cleaned)] 91 + [result (analyze parsed)]) 92 + (format-output result))) 93 + 94 + ;; RIGHT — flat defines in a body context 95 + (define (process data) 96 + (define cleaned (clean data)) 97 + (define parsed (parse cleaned)) 98 + (define result (analyze parsed)) 99 + (format-output result)) 100 + ``` 101 + 102 + This is the Racket style guide's explicit recommendation. `let*` is appropriate when you need sequential scope explicitly, but a series of `define` forms in a function body is preferred for readability. 103 + 104 + **Warning**: `define` in a body context has mutually recursive scope (like `letrec`), not sequential scope (like `let*`). This matters if bindings reference each other. In practice, this rarely causes issues for value bindings but is important for function bindings. 105 + 106 + ### 4. Use `for` loops, not `map`/`filter` with lambda 107 + 108 + ```racket 109 + ;; OK but not preferred 110 + (map (lambda (x) (* x x)) (filter even? lst)) 111 + 112 + ;; BETTER — for loop is more readable and general 113 + (for/list ([x (in-list lst)] 114 + #:when (even? x)) 115 + (* x x)) 116 + ``` 117 + 118 + The `for` family of loops (`for/list`, `for/fold`, `for/hash`, `for/sum`, `for/and`, `for/or`, `for/first`, `for/vector`, etc.) is the idiomatic way to iterate in Racket. They: 119 + - Avoid intermediate lambdas 120 + - Generalize across sequence types (lists, vectors, hashes, ranges, etc.) 121 + - Support `#:when`, `#:unless`, `#:break`, `#:final` clauses 122 + - Can iterate over multiple sequences in parallel 123 + 124 + **Important**: Always use `in-list`, `in-vector`, `in-hash`, `in-range`, etc. in `for` clauses for both clarity and performance. These are macros created with `define-sequence-syntax` and are recognized specially by `for` at compile time, enabling optimized iteration. A bare list variable in a `for` clause works but falls back to the generic (slower) sequence protocol. 125 + 126 + Note that `for` loops (but NOT `for*` loops) iterate multiple sequence clauses in lockstep. This is useful for pairing a sequence with its indices: 127 + 128 + ```racket 129 + ;; Find the index of the first comma in a string starting from pos 130 + (for/first ([c (in-string line pos)] 131 + [i (in-naturals pos)] 132 + #:when (char=? c #\,)) 133 + i) 134 + ``` 135 + 136 + Do NOT use `for/or` with `(and condition value)` as a workaround for this — use `for/first` with a second lockstep clause instead. 137 + 138 + ```racket 139 + ;; WRONG — bare list, slower and less clear 140 + (for/list ([x xs]) (add1 x)) 141 + 142 + ;; RIGHT — explicit sequence constructor 143 + (for/list ([x (in-list xs)]) (add1 x)) 144 + ``` 145 + 146 + ### 5. Use `match` for destructuring 147 + 148 + ```racket 149 + ;; WRONG — positional accessors 150 + (define (point-distance p) 151 + (sqrt (+ (sqr (point-x p)) (sqr (point-y p))))) 152 + 153 + ;; RIGHT — match-define for clean destructuring 154 + (define (point-distance p) 155 + (match-define (point x y) p) 156 + (sqrt (+ (sqr x) (sqr y)))) 157 + 158 + ;; ALSO RIGHT — match in function position 159 + (define (describe-result result) 160 + (match result 161 + [(success value) (format "OK: ~a" value)] 162 + [(failure msg) (format "Error: ~a" msg)])) 163 + ``` 164 + 165 + ### 6. Closing parentheses go on the SAME line 166 + 167 + ```racket 168 + ;; WRONG — C/Java style brace placement 169 + (define (factorial n) 170 + (if (zero? n) 171 + 1 172 + (* n (factorial (sub1 n))) 173 + ) 174 + ) 175 + 176 + ;; RIGHT — closing parens on the last line of the expression 177 + (define (factorial n) 178 + (if (zero? n) 179 + 1 180 + (* n (factorial (sub1 n))))) 181 + ``` 182 + 183 + This is non-negotiable in Racket. Never put closing parentheses on their own line. 184 + 185 + ### 7. Comments follow Lisp/Racket conventions 186 + 187 + ```racket 188 + ;;;; File-level header (rare) 189 + 190 + ;;; Section header 191 + 192 + ;; Normal comment explaining the next form or block 193 + (define (foo x) 194 + (+ x 1)) ; inline comment for a specific expression 195 + ``` 196 + 197 + - `;` — inline comments (end of line) 198 + - `;;` — line comments (start of line, most common) 199 + - `;;;` — section headers 200 + - `;;;;` — file headers (rare) 201 + - `#;` — comment out one s-expression (very useful for debugging) 202 + - `#| ... |#` — block comments (rare, for commenting out large blocks) 203 + 204 + ### 8. Naming conventions 205 + 206 + - Use `kebab-case` (hyphenated): `my-function`, `parse-input`, `default-timeout` 207 + - Do NOT use `snake_case` or `camelCase` 208 + - Predicates end with `?`: `empty?`, `string?`, `valid-input?` 209 + - Mutating procedures end with `!`: `set!`, `vector-set!`, `hash-set!` 210 + - Conversion functions use `->`: `string->number`, `list->vector` 211 + - Type/struct names are nouns: `point`, `employee`, `syntax-token` 212 + - Do NOT use `_` (underscore) in names — it's reserved for "don't care" patterns in `match` and `syntax-parse` 213 + - Avoid plural collection names: prefer `racket/list` not `racket/lists` 214 + 215 + --- 216 + 217 + ## Project Structure 218 + 219 + ### Single-collection package (most common) 220 + 221 + ``` 222 + my-package/ 223 + ├── info.rkt ; Package metadata 224 + ├── main.rkt ; Primary module (require'd as just "my-package") 225 + ├── private/ ; Internal modules not intended for external use 226 + │ ├── helper.rkt 227 + │ └── util.rkt 228 + ├── scribblings/ ; Documentation 229 + │ └── my-package.scrbl 230 + └── test/ ; Tests (or use module+ test inline) 231 + └── test-main.rkt 232 + ``` 233 + 234 + ### info.rkt for a single-collection package 235 + 236 + ```racket 237 + #lang info 238 + 239 + (define collection "my-package") 240 + (define version "0.1") 241 + (define deps '("base")) 242 + (define build-deps '("rackunit-lib" "scribble-lib" "racket-doc")) 243 + (define scribblings '(("scribblings/my-package.scrbl"))) 244 + (define pkg-desc "Short description of the package") 245 + (define license 'Apache-2.0) 246 + ``` 247 + 248 + ### Multi-collection package 249 + 250 + ``` 251 + my-package/ 252 + ├── info.rkt ; (define collection 'multi) 253 + ├── my-lib/ 254 + │ ├── info.rkt ; Collection-level info 255 + │ ├── main.rkt 256 + │ └── private/ 257 + ├── my-lib-test/ 258 + │ ├── info.rkt 259 + │ └── tests.rkt 260 + └── my-lib-doc/ 261 + ├── info.rkt 262 + └── scribblings/ 263 + ``` 264 + 265 + ### Key commands 266 + 267 + - `raco pkg install` — install a package (from current directory or source) 268 + - `raco pkg install --link` — link a local development directory as a package 269 + - `raco setup` — recompile and rebuild *all* installed Racket code. This is the most common workflow during development — no need to track which specific collections changed. 270 + - `raco setup <collection>` — recompile and rebuild only a specific collection 271 + - `raco test <file-or-directory>` — run tests 272 + - `raco test --drdr <file-or-directory>` — run tests in DrDr mode, which parallelizes test execution and is slightly stricter. This is the default mode used by the Racket package catalog build server, so test with this flag before publishing. 273 + - `raco test -p <package>` — run all tests in a package 274 + - `raco docs` — view local documentation 275 + - `raco pkg new <name>` — generate a new package skeleton 276 + 277 + --- 278 + 279 + ## Testing 280 + 281 + ### Inline tests with module+ test (preferred) 282 + 283 + ```racket 284 + #lang racket 285 + 286 + (provide double) 287 + 288 + (define (double x) 289 + (* x 2)) 290 + 291 + (module+ test 292 + (require rackunit) 293 + (check-equal? (double 5) 10) 294 + (check-equal? (double 0) 0) 295 + (check-equal? (double -3) -6)) 296 + ``` 297 + 298 + The `module+ test` submodule is the idiomatic way to write tests in Racket. Tests live alongside the code they test but only run when explicitly invoked via `raco test`. 299 + 300 + Multiple `module+ test` blocks in the same file are combined into a single test submodule — you can place test blocks near the functions they test. This is a good way to keep files easy to scan. Place the `(module+ test (require rackunit))` part in its own block near the top of the file, alongside the other `require` statements: 301 + 302 + ```racket 303 + #lang racket/base 304 + 305 + (require racket/list) 306 + 307 + (provide my-function) 308 + 309 + (module+ test 310 + (require rackunit)) 311 + 312 + (define (my-function x) 313 + (* x 2)) 314 + 315 + (module+ test 316 + (test-case "my-function" 317 + (test-case "doubles positive numbers" 318 + (check-equal? (my-function 5) 10)) 319 + (test-case "handles zero" 320 + (check-equal? (my-function 0) 0)))) 321 + 322 + (define (helper y) 323 + (+ y 1)) 324 + 325 + (module+ test 326 + (test-case "helper" 327 + (test-case "increments by one" 328 + (check-equal? (helper 5) 6)))) 329 + ``` 330 + 331 + Group all test cases for a function under a single `test-case` named after the function. Test cases can be nested in a tree structure, and their names affect the output of testing tools. 332 + 333 + ### rackunit essentials 334 + 335 + ```racket 336 + (require rackunit) 337 + 338 + ;; Basic checks 339 + (check-equal? actual expected) ; equality 340 + (check-not-equal? actual expected) ; inequality 341 + (check-true expr) ; must be #t 342 + (check-false expr) ; must be #f 343 + (check-pred predicate value) ; (predicate value) must be true 344 + (check-exn exn:fail? thunk) ; thunk must raise the given exception 345 + (check-not-exn thunk) ; thunk must not raise 346 + 347 + ;; Test cases for grouping (preferred) 348 + (test-case "descriptive name" 349 + (check-equal? (foo 1) 2) 350 + (check-equal? (foo 2) 4)) 351 + ``` 352 + 353 + **Avoid `test-suite`.** Test suites predate the submodule system and are rarely useful in modern Racket. They add complexity without much benefit — just use `test-case` for grouping and `module+ test` for structure. 354 + 355 + ### Separate test files 356 + 357 + For larger test suites, create a `test/` directory. **Always put tests in `module+ test` submodules**, even in files within a `test/` directory. This eliminates all ambiguity about what is test code vs. non-test code, and ensures tools (like `raco test`, `raco cover`, code stripping for binary packages) treat test code uniformly regardless of where it lives in the file system. 358 + 359 + --- 360 + 361 + ## Macros 362 + 363 + Racket has a powerful, hygienic macro system. There is a hierarchy of macro-definition forms: 364 + 365 + ### When to use macros vs functions 366 + 367 + **Use functions when possible.** The Racket style guide is explicit: do not introduce macros when functions will do. Functions compose better, are usable in higher-order contexts, and are simpler to understand. 368 + 369 + Use macros when you need to: 370 + - Control evaluation order (short-circuiting, delayed evaluation) 371 + - Introduce new binding forms 372 + - Transform syntax (new language constructs) 373 + - Generate compile-time errors for invalid usage 374 + - Work at the level of syntax rather than values 375 + 376 + ### Macro definition forms 377 + 378 + **Always use the `syntax-parse` macro system.** The `syntax-rules` and `syntax-case` systems are functionally obsolete in modern Racket. They lack `syntax-parse`'s automatic error reporting, syntax classes, and pattern directives. 379 + 380 + 1. **`define-syntax-parse-rule`** — simplest, for pattern-only macros (one clause) 381 + 382 + ```racket 383 + (require (for-syntax racket/base syntax/parse)) 384 + 385 + (define-syntax-parse-rule (swap! a:id b:id) 386 + (let ([tmp a]) 387 + (set! a b) 388 + (set! b tmp))) 389 + ``` 390 + 391 + Note: `define-syntax-parse-rule` is the modern name for `define-simple-macro`. Use the former. 392 + 393 + 2. **`define-syntax` + `syntax-parse`** — full power, for anything with multiple clauses, side conditions, or complex logic 394 + 395 + ```racket 396 + (require (for-syntax racket/base syntax/parse)) 397 + 398 + (define-syntax (my-cond stx) 399 + (syntax-parse stx 400 + [(_ [test:expr body:expr ...+] ...+) 401 + #'(cond [test body ...] ...)])) 402 + ``` 403 + 404 + Do NOT use `define-syntax-rule` for anything nontrivial — it provides no syntax class annotations and produces poor error messages. Prefer `define-syntax-parse-rule` even for simple cases. 405 + 406 + ### syntax-parse best practices 407 + 408 + `syntax-parse` is the modern, recommended way to write macros in Racket. It provides: 409 + - Syntax classes for reusable pattern components 410 + - Automatic error reporting with good messages 411 + - Pattern directives (`#:when`, `#:with`, `#:fail-when`, etc.) 412 + - Backtracking across clauses 413 + 414 + ```racket 415 + ;; Define reusable syntax classes 416 + (begin-for-syntax 417 + (define-syntax-class binding-pair 418 + #:description "binding pair" 419 + (pattern [name:id value:expr]))) 420 + 421 + (define-syntax (my-let stx) 422 + (syntax-parse stx 423 + [(_ (b:binding-pair ...) body:expr ...+) 424 + #'(let ([b.name b.value] ...) body ...)])) 425 + ``` 426 + 427 + Key points: 428 + - Always `(require (for-syntax racket/base syntax/parse))` — these are not automatically available 429 + - Use `:expr`, `:id`, `:keyword`, `:str`, `:nat`, etc. for built-in syntax classes 430 + - Use `...` (ellipsis) for repetition, `...+` for one-or-more 431 + - Use `#:literals` to match specific identifiers 432 + - Use `~and`, `~or`, `~seq`, `~optional` for complex patterns 433 + - Use `#'template` (shorthand for `(syntax template)`) to produce output 434 + 435 + ### Phase levels 436 + 437 + Racket has a strict phase separation system. Code that runs at compile time (phase 1) must be explicitly required at that phase. 438 + 439 + ```racket 440 + (require (for-syntax racket/base)) ; phase 1 (compile time) 441 + (require (for-template racket/base)) ; phase -1 (used in code generation) 442 + (require (for-meta 2 racket/base)) ; phase 2 (macros that define macros) 443 + ``` 444 + 445 + Common phase-level mistakes: 446 + - Forgetting `(require (for-syntax racket/base))` when writing macros 447 + - Trying to use a runtime binding at compile time 448 + - Forgetting that `begin-for-syntax` runs at phase 1 449 + 450 + --- 451 + 452 + ## Module System 453 + 454 + ### require and provide 455 + 456 + ```racket 457 + #lang racket 458 + 459 + ;; Provide specific bindings 460 + (provide function-a 461 + function-b 462 + struct-name) 463 + 464 + ;; Provide everything 465 + (provide (all-defined-out)) 466 + 467 + ;; Provide with contracts 468 + (provide 469 + (contract-out 470 + [function-a (-> string? number?)] 471 + [struct-name ([field-a string?] [field-b number?])])) 472 + 473 + ;; Require from collections 474 + (require racket/list ; specific collection module 475 + racket/match ; usually already in #lang racket 476 + "private/helper.rkt" ; relative path 477 + (only-in racket/string string-join string-split) 478 + (prefix-in http: net/http-easy)) 479 + ``` 480 + 481 + ### #lang line 482 + 483 + Every Racket source file starts with `#lang`: 484 + 485 + ```racket 486 + #lang racket ; full Racket language 487 + #lang racket/base ; minimal Racket (faster to load, explicit requires) 488 + #lang typed/racket ; Typed Racket 489 + #lang scribble/manual ; documentation 490 + #lang info ; package metadata (info.rkt files) 491 + ``` 492 + 493 + Use `#lang racket/base` for library code when you want faster startup and explicit dependencies. Use `#lang racket` for scripts and applications where convenience matters more. 494 + 495 + --- 496 + 497 + ## Common Patterns and Standard Library 498 + 499 + ### Hash tables 500 + 501 + ```racket 502 + ;; Immutable hashes (preferred) 503 + (define h (hash "a" 1 "b" 2 "c" 3)) 504 + (hash-ref h "a") ; => 1 505 + (hash-set h "d" 4) ; => new hash with "d" added 506 + (hash-update h "a" add1) ; => new hash with "a" incremented 507 + 508 + ;; Iterating 509 + (for ([(k v) (in-hash h)]) 510 + (printf "~a: ~a\n" k v)) 511 + 512 + ;; Building from iteration 513 + (for/hash ([x (in-list items)]) 514 + (values (item-key x) (item-value x))) 515 + ``` 516 + 517 + ### Structs 518 + 519 + ```racket 520 + ;; Basic struct 521 + (struct point (x y) #:transparent) 522 + 523 + ;; With contracts and methods 524 + (struct employee (name salary department) 525 + #:transparent 526 + #:methods gen:custom-write 527 + [(define (write-proc emp port mode) 528 + (fprintf port "<employee:~a>" (employee-name emp)))]) 529 + ``` 530 + 531 + `#:transparent` makes structs printable and comparable — use it unless you have a reason not to. 532 + 533 + ### String operations 534 + 535 + ```racket 536 + (require racket/string) 537 + 538 + (string-join '("a" "b" "c") ", ") ; => "a, b, c" 539 + (string-split "a,b,c" ",") ; => '("a" "b" "c") 540 + (string-contains? "hello world" "world") ; => #t 541 + (string-prefix? "hello" "hel") ; => #t 542 + (string-trim " hello ") ; => "hello" 543 + (~a 42) ; => "42" (any value to string) 544 + (~v '(1 2 3)) ; => "'(1 2 3)" (verbose/print-style) 545 + ``` 546 + 547 + ### Formatting output 548 + 549 + ```racket 550 + ;; format returns a string 551 + (format "Name: ~a, Age: ~a" name age) 552 + 553 + ;; printf prints directly 554 + (printf "Processing ~a of ~a\n" current total) 555 + 556 + ;; ~a = display style (human-readable) 557 + ;; ~v = print style (Racket-readable, like ~s but modern) 558 + ;; ~s = write style (legacy Racket-readable) 559 + ;; ~e = error-message style (rarely useful; see fprintf docs) 560 + ``` 561 + 562 + ### Error handling 563 + 564 + ```racket 565 + ;; Raising errors — prefer raise-argument-error, raise-arguments-error, etc. 566 + ;; These produce messages conforming to Racket's Error Message Conventions. 567 + ;; See: https://docs.racket-lang.org/reference/exns.html#%28part._err-msg-conventions%29 568 + (raise-argument-error 'function-name "positive integer" arg) 569 + (raise-arguments-error 'function-name 570 + "index out of range" 571 + "index" idx 572 + "valid range" (format "[0, ~a)" len)) 573 + 574 + ;; Avoid `error` — it is lightly discouraged in modern Racket because its 575 + ;; format-string style doesn't conform to Racket's error message conventions. 576 + ;; Use raise-argument-error, raise-arguments-error, or raise-result-error instead. 577 + ;; (error 'fn "expected ~a, got ~a" expected actual) ; avoid this 578 + 579 + ;; Handling errors 580 + (with-handlers ([exn:fail:filesystem? (lambda (e) (default-value))] 581 + [exn:fail:network? (lambda (e) (retry))]) 582 + (do-risky-operation)) 583 + ``` 584 + 585 + ### Working with paths and files 586 + 587 + ```racket 588 + (require racket/path) 589 + 590 + (define p (build-path "src" "main.rkt")) 591 + (path->string p) 592 + (file-exists? p) 593 + (directory-list "src") 594 + (make-directory* "path/to/new/dir") ; like mkdir -p 595 + 596 + ;; Reading/writing files 597 + (file->string "data.txt") 598 + (file->lines "data.txt") 599 + (display-to-file content "output.txt" #:exists 'replace) 600 + 601 + ;; Port-based I/O 602 + (call-with-input-file "data.txt" 603 + (lambda (in) 604 + (for/list ([line (in-lines in)]) 605 + (process-line line)))) 606 + ``` 607 + 608 + --- 609 + 610 + ## Formatting Rules 611 + 612 + - Maximum line width: 102 characters (per Racket style guide). Prefer 80 if feasible. 613 + - Do NOT use tab characters. Use spaces only. 614 + - Indent subexpressions by 2 spaces when they are body forms. 615 + - Align arguments vertically when they overflow a line. 616 + - All closing parentheses go on the same line as the last expression. 617 + - Put a blank line between top-level definitions. 618 + - Put the `#lang` line first, then `require`s, then `provide`s, then definitions. 619 + 620 + ```racket 621 + #lang racket 622 + 623 + (require racket/string 624 + racket/list) 625 + 626 + (provide process-data 627 + format-result) 628 + 629 + (define (process-data data) 630 + ...) 631 + 632 + (define (format-result result) 633 + ...) 634 + ``` 635 + 636 + --- 637 + 638 + ## Third-Party Tools 639 + 640 + These tools are widely used by Racketeers (the Racket community's name for themselves, in the Scheme tradition of naming things after illicit activities): 641 + 642 + - **`raco fmt`** — automatic code formatting. Use this to ensure consistent formatting rather than hand-formatting. 643 + - **`resyntax`** — analyzes Racket code and suggests idiomatic improvements. Powered by `syntax-parse`-based refactoring rules. Useful for both learning idiomatic Racket and automated code cleanup. 644 + - **`raco cover`** — generates code coverage reports from tests. Helps identify untested code paths. 645 + 646 + --- 647 + 648 + ## Pitfalls Checklist 649 + 650 + Before submitting Racket code, verify: 651 + 652 + - [ ] File starts with `#lang racket` (or appropriate language) 653 + - [ ] Square brackets used in cond, match, let, for, parameterize clauses 654 + - [ ] No closing parens on their own line 655 + - [ ] Using `for/list` etc. instead of manual recursion where appropriate 656 + - [ ] Using `in-list`, `in-vector`, etc. in for clauses 657 + - [ ] Kebab-case naming throughout (no snake_case, no camelCase) 658 + - [ ] Predicates end in `?`, mutators in `!`, conversions use `->` 659 + - [ ] `define` used instead of `let*` where possible in function bodies 660 + - [ ] Structs use `#:transparent` unless there's a reason not to 661 + - [ ] Tests in `module+ test` submodules (even in test/ directories) 662 + - [ ] `require` and `provide` at the top of the file 663 + - [ ] No unnecessary dependencies (use `racket/base` + explicit requires for libraries) 664 + - [ ] Errors raised with `raise-argument-error` / `raise-arguments-error`, not `error` 665 + - [ ] Macros use `syntax-parse`, not `syntax-rules` or `syntax-case`