Racket-specific guidance for the carmack agent including: - Idiomatic Racket patterns (match, for/*, cond) - Module organization with explicit provide lists - Contracts and type boundaries - Data modeling with structs - Error handling best practices - IO, paths, and portability - Performance considerations - Macro guidelines - Testing with rackunit
3.4 KiB
3.4 KiB
RACKET-SPECIFIC GUIDANCE (apply by default)
-
Prefer idiomatic Racket:
- 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.
- Use
-
Modules and structure:
- Organize code into small modules with explicit
providelists (preferprovide (contract-out ...)when exporting). - Avoid
provide (all-defined-out)except in quick prototypes/tests. - Prefer
requirewith explicit identifiers; avoid huge wildcard imports.
- Organize code into small modules with explicit
-
Contracts and types:
- If in untyped Racket: add contracts at module boundaries for public APIs (
contract-out), 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.
- If in untyped Racket: add contracts at module boundaries for public APIs (
-
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. - For “records loaded from YAML/JSON”: validate once at the boundary; keep internal representation consistent.
- Prefer
-
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). - Don’t swallow exceptions; surface actionable diagnostics.
- Use
-
IO, paths, and portability:
- Use
build-path,simplify-path,path->stringas needed; don’t concatenate path strings manually. - Use
call-with-input-file/call-with-output-fileand ports idiomatically. - Prefer
file/sha1,file-watch-style libs (if present) for reload tooling; otherwise design a simple polling fallback.
- Use
-
Performance + allocations:
- 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. - If profiling is needed: use
profileortime, and optimize the bottleneck only.
-
Macros and syntax:
- 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.
-
Testing + docs:
- Add
rackunittests for tricky logic; prefer table-driven tests. - When writing public APIs, add docstrings/comments; if there’s a lib boundary, consider Scribble docs.
- Include runnable examples in comments when it helps.
- Add
-
Concurrency/events (common in engines/tools):
- Prefer clear event loops and message passing; avoid shared mutable state unless protected.
- If using parameters (
parameterize), keep scope tight and document effects.
-
Packages/tooling:
- Assume
raco fmt/racket-formatstyle; keep formatting consistent. - If suggesting deps, name the package and
raco pkg installusage.
- Assume
-
Output expectations:
- When proposing code changes, include: new/changed function signatures, required
requires, and small usage examples. - If unsure about a library’s availability, provide a fallback approach that uses base Racket.
- When proposing code changes, include: new/changed function signatures, required