一键导入
verona-coding
Patterns, pitfalls, and idioms for writing Verona source code (.v files). Use when writing library code, _builtin types, or user programs.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Patterns, pitfalls, and idioms for writing Verona source code (.v files). Use when writing library code, _builtin types, or user programs.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Verona compiler coding conventions, C++ style, trieste patterns, and project structure. Applies when writing or reviewing Verona compiler code.
Structured debugging protocol with checkpoints. Load when debugging non-trivial issues — before forming any hypothesis about the cause.
Verona compiler test suite infrastructure — running tests, updating golden files, verifying pass completeness, checking error codes. Use when debugging test failures, regenerating golden files, or understanding the test framework.
Patterns, pitfalls, and idioms for writing Verona source code (.v files). Use when writing library code, _builtin types, or user programs.
Verona compiler type inference model — bidirectional refinement, per-function inference, lambda/context propagation, algebraic and structural typing, generics, and debugging. Use when changing or debugging `vc/passes/infer.cc` or investigating type inference failures.
Create a new Verona compiler test case. Use when the user wants to add a test, create a test, or scaffold a test for the compiler.
| name | verona-coding |
| description | Patterns, pitfalls, and idioms for writing Verona source code (.v files). Use when writing library code, _builtin types, or user programs. |
Patterns, pitfalls, and idioms for writing Verona source code. Use this skill
when writing .v files — library code, _builtin types, or user programs.
u64 0, i32 3). Let type
inference determine the type from context. If inference fails, that's a
compiler bug to fix — report it rather than working around it.usize | none) don't
refine bare literals. return 0 in a function returning usize | none
gives u64, not usize. This is a compiler bug to be fixed.compare() returns i64: comparing with bare 0 works — inference
resolves from the i64 return type of compare().a min b).cap < n + 1 evaluates as (cap < n) + 1. Use parens
to group: cap < (n + 1).a(i) binds before any infix
operator, so sum + a(i) works as expected.|, bool and is &. Since there's no precedence,
parenthesize each comparison: (c == 32) | (c == 9) | (c == 10).string::is_space(c), not
is_space(c). Unqualified names resolve as method calls on the first arg.self.data(i) calls method data with arg i,
NOT field access + apply.(): Write self.data not self.data(),
self.size not self.size(). Parens are unnecessary for zero-extra-arg
methods (only self).self.data()(i) — the first () is needed to
disambiguate from self.data(i) (which calls method data with arg i).
But when chaining further: self.data.pairs ... — no parens needed on
data because pairs is consumed by dot, not by juxtaposition.result(i) works directly (no field access ambiguity).ref keyword on functions: ref apply(...) returns a ref[T], enabling
both read and write through the result. Use ref self.data()(index) to
delegate ref access.(params) -> { body }. Examples:
(x: i32) -> { x + 1 }(i, c) -> { ... } (types inferred from context){ body } (zero-arg lambda)self.data.pairs (i, c) -> {
...
}
NOT self.data.pairs((i, c) -> { ... }) — the extra parens are unnecessary.} ends the expression).raise in a lambda is a non-local return — it exits the ENCLOSING
function, not the lambda. The lambda's own return type is unaffected by
raise.freeze — any object can be mutable or
permanently immutable. This is NOT like C++ const.string::create() and then freezes the result. The frozen string directly
references the constant pool array — no copy.create() should NOT copy the input array. It just wraps it:
create(data: array[u8]): string
{
new { data, len = data.size - 1 }
}
' ' causes parser issues in nested control flow. Use the
numeric value 32 instead. Other char literals like 'a' work fine.usize | none is a union type. Callers consume it with match/else:
match s.find("x") { (i: usize) -> i; } else { 99 }
dyn. Copy to a typed let first (see Literal Types
section above).match expr
{
(pattern) -> body;
}
else
{
default
}
Not match expr { (pattern) -> body; } else { default } on one line.Use bulk operations over byte-by-byte loops: array.copy_from() wraps
memmove (handles overlapping regions). Use it for shifting data within the
same array instead of while loops.
Avoid double-moving data: replace should shift the tail once to its
final position and copy the replacement in, not call erase then insert
(which shifts the tail twice).
copy_from for self-overlapping copies: self.data.copy_from(dst, self.data, src, len)
works correctly for overlapping regions — it's memmove, not memcpy.
none — no parens needed. Write none not none().
Don't use () unnecessarily: if a type or value needs no arguments,
omit the parens. none, true, false, not none(), true(), false().
Empty string: string(array[u8]::fill(1)) — a 1-byte array containing
just the null terminator, with len=0.
if/while/for.new { field = val } — no class name after new.Type(args) is sugar for Type::create(args).array[u8]::fill(n) allocates an array of size n (zero-filled).self.len calls the getter (zero-arg method, no parens needed).f() calls a zero-arg callable value. Don't drop the parens on lambda/function
values just because zero-arg methods omit them.handle::signal, tty, self.init handler, pipe::open 0.self.len = x calls the setter.final(self: T) receives a read-only self. Don't call mutating helpers like
self.close from a finalizer; inline read-only-safe cleanup instead.var result = 0;
if cond1_fails { result = result + 1 }
if cond2_fails { result = result + 2 }
// ...powers of 2...
result // exit code 0 means all passed
use "_builtin"._activate(active) plus handler-safe deferred release
(_finish_handler) so ffi::pin / ffi::unpin and
ffi::external.add / ffi::external.remove track active handles or pending
requests.pipe.open(fd)), don't route the error
through an active-only dispatch helper — use a separate ungated failure
callback path._state directly.