Changes: - Add concrete code examples for match/cond and contract-out - Add Phase separation section (for-syntax vs runtime) - Add Continuations section (call/ec over call/cc, parameterize) - Add Concurrency section (places, threads, channels, sync) - Add Gotchas section (eq?/equal?/eqv?, null?/empty?, string=?) - Tighten Packages/tooling (raco pkg install --auto, info.rkt) Removed generic advice: - 'Don't swallow exceptions' (obvious) - 'Add docstrings/comments' (obvious) - 'Include runnable examples' (obvious) - 'Optimize the bottleneck only' (obvious) - Entire 'Output expectations' section (meta, not Racket-specific) - Removed oddly specific 'file/sha1, file-watch' reference
3.7 KiB
3.7 KiB
Prefer obvious, readable Racket over cleverness.
Keep control flow clean
;; Good: match for destructuring
(match-define (list name age) (get-user-info id))
;; Good: cond over nested if
(cond
[(empty? items) '()]
[(special? (first items)) (handle-special items)]
[else (process-normal items)])
- Use
match/match-definefor destructuring. - Use
for/*loops and sequences instead of manual recursion unless recursion is clearer. - Use
cond,case,and,orcleanly; avoid nestedifpyramids. - Use immutable data by default; reach for mutation only when it materially improves clarity/perf.
Modules: explicit exports
;; Good: explicit contract-out
(provide
(contract-out
[process-data (-> input/c output/c)]))
;; Bad: leaky exports
(provide (all-defined-out))
- Organize code into small modules with explicit
providelists. - Prefer
contract-outwhen exporting public APIs. - Avoid
provide (all-defined-out)except in quick prototypes/tests. - Prefer
requirewith explicit identifiers; avoid huge wildcard imports.
Contracts and types
- If in untyped Racket: add contracts at module boundaries for public APIs, especially for callbacks and data shapes.
- If the project uses
typed/racket, keep typed/untyped boundaries clean and document them. - Use predicates + struct definitions to make data models explicit.
Data modeling
- Prefer
struct(possibly#:transparent) for domain objects, not ad-hoc hash soup. - For enums/variants: consider
structvariants +match, or symbols with clear validation. - Validate external data (YAML/JSON) once at the boundary; keep internal representation consistent.
Error handling
- Use
raise-argument-error,raise-user-error, orerrorwith a clear message. - Wrap IO and parsing with
with-handlersand rethrow with context (what file, what phase).
IO and paths
- Use
build-path,simplify-path,path->string; don't concatenate path strings manually. - Use
call-with-input-file/call-with-output-fileidiomatically.
Performance
- Prefer vectors for hot loops / indexed access; lists for iteration; hashes for keyed lookup.
- Use
for/foldorfor/hashto build results efficiently. - Avoid repeated
appendin loops; accumulate then reverse if needed.
Macros: use sparingly
- Don't write macros unless it meaningfully reduces boilerplate or enforces invariants.
- If writing macros: use
syntax-parse(not rawsyntax-case) and include good error messages. - Keep macro output readable and debuggable.
Phase separation
- Understand
for-syntaxvs runtime; don't accidentally pull runtime values into macros. - Use
begin-for-syntaxsparingly; prefersyntax-local-valuepatterns when possible.
Continuations: use sparingly
- Prefer
call/ec(escape continuations) over fullcall/ccwhen possible. - Use
parameterizefor dynamic scope, not continuation tricks. - If using
parameterize, keep scope tight and document effects.
Concurrency
- Use
places for CPU parallelism,threads for I/O concurrency. - Prefer channels (
make-channel,channel-put,channel-get) over shared state. - Use
syncand events for composable waiting.
Gotchas
eq?vsequal?vseqv?: useequal?by default for structural comparison.null?only works on proper lists; useempty?fromracket/listfor generics.string=?notequal?for string comparison in hot paths.
Testing
- Add
rackunittests for tricky logic; prefer table-driven tests. - Consider Scribble docs for library boundaries.
Packages/tooling
- Assume
raco fmtstyle; keep formatting consistent. - Use
raco pkg install --autofor dependency resolution. - Prefer
info.rktfor package metadata over ad-hoc scripts.