Enforce idiomatic, readable Nim with strict control-flow, template/proc boundaries, type modeling, and naming rules.
a..b, a..<b, and a..^b. Add spaces
when an operand contains an operator, as in a .. -3.range type constructors, use ..: range[0..n-1] is valid;
range[0..<n] is invalid.std/... imports for stdlib modules.import std/[a, b, c], import lib/[x, y, z].from foo import bar, baz when you only need a small API slice.import brings symbols into scope without qualification.export takes only the module name: export baz, not export foo/bar/baz.module.symbol access only to resolve genuine name conflicts between imported modules.PascalCase.camelCase.PascalCase; camelCase is also allowed.ALL_UPPERCASE only when preserving names from a C or C++ wrapper.parseUrl and httpStatus.Product.name, not Product.productName.inv: Inventory or s: Session.Obj, Ref, or Ptr.Error and programming-bug defect types with Defect.pcFile. For pure enums, use PascalCase.m, as in mitems and mpairs.reverse and reversed. If the copy
name already exists, suffix the in-place form with In, as in replaceIn.fileExists, not existsFile.initX, newX, find,
contains, add, cmp, len, cap, items, pairs, incl, and excl.foo. Use getFoo when the operation has side
effects or is not O(1).foo with foo= and getFoo with setFoo.proc.func for pure helpers and pure accessors when checked purity helps.template when call-site substitution, lazy evaluation, or a
control-flow abstraction is required.proc and func over method. Use method only when you need runtime dispatch.{.nimcall.}.macro only when syntax transformation is required.{.inline.}.typeof(x), not the historical type(x).addr x, not the deprecated unsafeAddr x.north, not Direction.north); qualify only on ambiguity.let by default.var only for values that mutate.result explicitly; prefer object constructors
(TypeName(field: value)) over field-by-field assignment.T() works only for objects; arrays use arrayWith, tuples use literals,
strings/seqs use ""/@[], default(T) as fallback.These whitespace choices change how Nim parses code:
Attach [ to a type name.
array[N, T] compiles. array [N, T] does not.seq[T], Table[K, V], etc.Attach ( to a callable name for a parenthesized call.
foo(1, 2) passes two int arguments.foo (1, 2) passes a single (int, int) tuple. Nim treats the parenthesized comma-list as a tuple constructor.Do not use command-call syntax with comparisons or nested calls.
foo 1 == 1 parses like foo(1 == 1), not foo(1) == 1.outer(inner 1, 1) parses as outer(inner(1), 1), not outer(inner(1, 1)).Use [:T] for explicit generic parameters in UFCS calls.
x.p[:T]() rewrites to p[T](x).x.p[T]() parses as (x.p)[T](), not a generic instantiation.Group negated compound expressions.
not: not a or b parses as (not a) or b, so write not (a or b) and not (x < y).x notin items, not not x in items.Nim has no line-continuation character. Break expressions after an operator
or comma, or within open delimiters such as (), [], and {}. Indent the
continued line further than the statement that started it.
For constructs with a body, double indentation can distinguish continuation lines from the body.
"""...""" triple-quoted literals instead of "a\n" & "b\n"
concatenation for multiline values.""". That newline is stripped, so the
value is identical; the content aligns with the next line.See references/multiline_strings.md.
func len(b: Bag): int = b.items.lenresult explicitly: as a default at the start, at the end, or once per branch.result may be overridden by the branch that produces the value.continue. Put the remaining loop body inside a condition.proc. Switch to func for a useful purity contract, template
for call-site substitution, and macro for syntax transformation. Choose
method for runtime dispatch.std/... imports, narrow imports when practical, and keep names in normal Nim casing.let by default, keep locals near first use, and keep reusable helpers at module scope.| Mistake | Why it is wrong |
|---------|-----------------|
| Using method as the default callable kind | It adds runtime dispatch where a plain proc or func would usually be clearer. |
| Hiding reusable helpers inside another proc | It makes the helper harder to reuse and easier to turn into an accidental closure. |
| Writing one argument per line by default | It adds vertical noise without adding structure. |
| Using var for values that never mutate | It hides which locals actually change. |
| Assuming {.pure.} requires Type.value | Pure enum values may be unqualified. |
| Turning every branch into an early return in a multi-step proc | It makes the normal path harder to scan. |
| Using continue | A structured branch keeps the loop invariant visible. |
references/core_patterns.md — General style patterns for imports, callable kinds, wrapping,
locals, and constructors.references/multiline_strings.md — Whitespace-preserving triple-quoted strings, joins, and
dedenting.Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer