nim-api-design
Skill4 starsChanged 3 months agoMITView on GitHub
Design clear public Nim APIs for libraries and modules, including exported types, constructors, parameter ownership, lookup functions, error contracts, and container-style interfaces. Use when creating or reviewing a Nim library API, designing an exported module surface, or deciding how callers should construct, pass, access, and validate data.
1 other project uses a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- planetis-m/skillsedited, first seen Jul 16, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 1
- planetis-m/skills★ 5994% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository18 other files it gives its agents.
- atlas-package-clonerSkill
- buildSkill
- docs-genSkill
- format-codeSkill
- nim-c-bindingsSkill
- nim-code-organizationSkill
- nim-c-wrappersSkill
- nim-debuggingSkill
- nim-defect-analysisSkill
- nim-doc-commentsSkill
- nim-error-handlingSkill
- nim-fuzzingSkill
- nim-mcp-toolsSkill
- nimonypluginsSkill
- nim-ownership-hooksSkill
- nim-style-guideSkill
- nim-testingSkill
- run-testsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
32 rules. About 0 are common, 4 are distinctive, and 28 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
28
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 12
Prefer plain object types for public data models.
- Very specific: line 13
Use ref object only when identity, aliasing, shared mutation, graph structure, or handle lifetime is part of the contract.
- Distinctive: line 17
Prefer procs, overloads, generics, and iterators.
- Very specific: line 19
Use named object types for public semantic data.
- Very specific: line 20
Use tuples only for local glue or iterator yields such as (key, val).
- Very specific: line 23
When indexed removal must preserve sequence order, use delete(idx), not del(idx).
- Very specific: line 24
Use find for an index or position result.
- Very specific: line 31
Use range types for constrained public parameters.
- Very specific: line 36
When an enum's string spelling is dictated by an external format or protocol, set explicit strings (dirNorth = "north").
- Very specific: line 38
Use static[T] in public APIs only when callers must supply a compile-time constant.
- Very specific: line 41
Use distinct when two values share a base type but must not mix.
- Very specific: line 42
If a public type can be used as a table or set key, define hash consistent with ==.
- Very specific: line 43
Use func for pure query operations when purity is part of the public contract.
- Very specific: line 44
Use {.raises: [].} when a proc must not raise.
- Very specific: line 48
Use initX() for value types that copy by value.
- Very specific: line 49
Use newX() for ref types and for value types with reference semantics.
- Very specific: line 50
Use one toX() name for common conversions.
- Distinctive: line 51
Use default parameter values for optional constructor arguments, keeping the no-argument call the main path.
- Very specific: line 56
When a parameter must accept both arrays and seqs, use openArray[T] for read-only traversal or var openArray[T] for fixed-length element …
- Very specific: line 59
When you control the callers, name the concrete type (array[N, T] or seq[T]) instead of openArray[T].
- Very specific: line 64
Use T when the caller's variable stays unchanged, var T when the proc changes it, sink T when the proc takes ownership, and lent T only for …
- Very specific: line 70
Use move(x) only where auto-sink does not apply: fields of var parameters, loop-indexed seq/array elements (arr[i]), and values reused …
- Very specific: line 72
Use ensureMove(x) to make an accidental copy a compile-time error.
- Very specific: line 78
Use contains or hasKey for membership checks and getOrDefault for explicit fallback values.
- Very specific: line 79
If several accessors fail the same way, route the failure through one private {.noinline, noreturn.} helper.
- Very specific: line 84
Use lent T for read accessors that return storage owned by the receiver.
- Distinctive: line 92
Keep accessor-backed fields private, so external dot access routes to the accessors.
- Distinctive: line 94
Use descriptive public names.
- Very specific: line 96
Keep template lookup escape hatches such as withValue secondary to the main lookup surface.
- Very specific: line 101
Start with a plain object.
- Very specific: line 103
Use named objects for semantic results and distinct types for domain identities.
- Very specific: line 105
Use initX, newX, and toX in the stdlib style.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Nim API Design
Rules
Public shape
- Prefer plain
objecttypes for public data models. - Use
ref objectonly when identity, aliasing, shared mutation, graph structure, or handle lifetime is part of the contract. - Export one primary public representation per concept. Add paired value/ref APIs only when both forms are part of the contract.
- Prefer procs, overloads, generics, and iterators. Do not default to methods or runtime dispatch for ordinary APIs.
- Use named
objecttypes for public semantic data. - Use tuples only for local glue or iterator yields such as
(key, val). - Reuse stdlib names when the behavior matches:
len,find,contains/hasKey,[],[]=,items/mitems,pairs/mpairs,add,del,clear,incl/excl, andpush/pop. - When indexed removal must preserve sequence order, use
delete(idx), notdel(idx). - Use
findfor an index or position result. UsecontainsorhasKeyfor boolean membership. - For collection-like types, expose
itemsandpairs. Addmitemsandmpairsonly when callers may safely mutate yielded values. - For comparable types, define
==,<and<=. Do not define!=,>, or>=; Nim derives them.
Contracts
- Use range types for constrained public parameters; store their values in the underlying type
(
intforNaturalorPositive). - Pass arguments directly to range parameters; conversion is implicit, so do not write
Positive(x). - Model closed choice sets as enums; add reserved members for future assignments in bounded protocol code spaces; use strings for open-ended or externally extensible token namespaces.
- When an enum's string spelling is dictated by an external format or protocol, set
explicit strings (
dirNorth = "north");$andparseEnum[T]always round-trip. - Use
static[T]in public APIs only when callers must supply a compile-time constant. - Bare
typedescparameters may name different types; shareTacrosstypedesc[T]parameters when they must match. - Use
distinctwhen two values share a base type but must not mix. - If a public type can be used as a table or set key, define
hashconsistent with==. - Use
funcfor pure query operations when purity is part of the public contract. - Use
{.raises: [].}when a proc must not raise. Leave raising procs unannotated.
Constructors and conversions
- Use
initX()for value types that copy by value. - Use
newX()for ref types and for value types with reference semantics. - Use one
toX()name for common conversions. Overload on input type. Do not define converters. - Use default parameter values for optional constructor arguments, keeping the no-argument call the main path.
Sequence parameters
- When a parameter must accept both arrays and seqs, use
openArray[T]for read-only traversal orvar openArray[T]for fixed-length element mutation. - Resizing or replacement requires
var seq[T]: anopenArrayis fixed-length. - When you control the callers, name the concrete type (
array[N, T]orseq[T]) instead ofopenArray[T].
Parameter ownership
- Use
Twhen the caller's variable stays unchanged,var Twhen the proc changes it,sink Twhen the proc takes ownership, andlent Tonly for borrowed returns. - Passing
x: Tnever copies whatxpoints to. Do not useptr Toraddr xto avoid a copy. - Pass sink arguments normally — do not wrap them in
move()orensureMove(). Last-use auto-sink moves locals, their fields, tuple fields and indices, and direct-indexed seq/array elements with no copy. - Use
move(x)only where auto-sink does not apply: fields ofvarparameters, loop-indexed seq/array elements (arr[i]), and values reused later in the same scope. - Use
ensureMove(x)to make an accidental copy a compile-time error.
Lookup surface
- Separate required lookup from optional lookup.
- A required lookup raises one specific catchable exception. It does not return a silent default.
- Use
containsorhasKeyfor membership checks andgetOrDefaultfor explicit fallback values. - If several accessors fail the same way, route the failure through one private
{.noinline, noreturn.}helper.
Borrowed and mutable access
- Use
lent Tfor read accessors that return storage owned by the receiver. - Return
var Tonly for deliberate mutable views. If changes require validation or related updates, expose mutation procs instead. - In
lentandvaraccessors, return directly from storage. Do not route through a temp local.
Public boundary
- Export only the stable surface. Keep helpers private.
- Keep accessor-backed fields private, so external dot access routes to the accessors; an exported field would bypass them.
- Use descriptive public names.
- In user code, gate version-specific API with
when (NimMajor, NimMinor) >= (x, y). - Keep template lookup escape hatches such as
withValuesecondary to the main lookup surface.
Workflow
- Choose the representation.
Start with a plain
object. Switch toref objectonly if the contract needs identity or aliasing. - Name the public types.
Use named objects for semantic results and
distincttypes for domain identities. - Name the constructor surface.
Use
initX,newX, andtoXin the stdlib style. - Design the lookup surface. Provide one strict path for required data and one explicit safe path for optional data.
- Choose parameter modes and borrowed access.
Use
Twhen the caller's variable stays unchanged,var Twhen the proc changes it,sink Twhen the proc takes ownership, andlent Tto return a borrow. Use mutation procs when changes need validation or related updates. - Verify the contract. Compile public examples. Exercise each mutation, ownership, and failure path.
Common Mistakes
| Mistake | Why it is wrong |
Starting with ref object for plain data | It adds aliasing and shared mutation where the API does not need them |
| Defaulting to methods or runtime dispatch | It hides behavior behind runtime polymorphism when a proc surface is simpler and clearer |
Weakening a constrained public parameter to int and re-checking manually | It throws away a stronger boundary contract |
| Returning a silent default for required data | It hides missing-data bugs |
Returning var T for controlled state | Callers can bypass validation and related updates |
Returning a lent or var result through a temp local | The temp dies at return; the compiler rejects the dangling borrow |
Using lent T for an input parameter | lent T is a borrowed return type; the compiler rejects it in parameter position |
| Assuming a sink call always moves the caller's variable | Nim copies the argument when it cannot prove last use |
Wrapping a routine sink argument in ensureMove | Sink already performs last-use analysis; use ensureMove only when the code must fail instead of copy |
Defining !=, >, or >= for a type | These override Nim's derived comparison templates and can make comparisons inconsistent |
References
references/representation_and_construction.md— Choosing value or reference semantics withinitXandnewXconstruction.references/lookup_and_mutation.md— Collection access through required, optional, borrowed, and controlled-mutation paths.references/parameter_and_result_shapes.md— Domain IDs, related options, constrained parameters, and named results in one public API.references/parameter_ownership.md— ChoosingopenArray,var,sink, andlentparameters.references/template_usage.md— Templates for scoped cleanup, caller-named access, and lazy evaluation that a proc cannot express.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

