This page is two readers' read of Vela: the language, the compiler, the
runtime, the library, the tests, the docs, and Vela Studio. GLM wrote
the first pass; Kimi revised it and added a second one. The first half
is GLM's voice, kept mostly as it was. The second half is Kimi's.
Nothing here changes the language; like
the second notebook, these are voices to
agree with or argue against.
What GLM would try
Each idea below says what it adds, what it costs, and how it fits the
rules the language already has. Two are already on the table in
another doc; those say so and take a position. None needs a borrow
checker, a trait system, or a second way to write something that
already has one.
A guard build: vela --guard
idea
medium
Safety lists, for every open concern, the
check a running program could do; they are written as separate "ways
it could go". One flag could turn them all on at once:
-
a call-depth counter, catching the runaway recursion that today is
a bare segmentation fault;
-
where a call gives a place behind a pointer beside another pointer
that may reach it, a check at the call that the two addresses
differ, trapping when they don't;
-
a generation number on each slice, raised by
add and
del, and a view (xs[a..b], a loop's own
copy) that reports when it outlives one.
- What it adds
-
The open concerns stop being reading material and become a mode a
program can run in while hunting a bug, the way the project's own
suite already runs everything under the sanitizers. Nothing
changes in the language; a trap in a guard build is a message
saying where, like every other stop.
- What it costs
-
A second runtime path to keep honest, and slower programs —
the same trade
--quick already asks about, in the
other direction. The checker is untouched, so the guard build is a
runtime and C-writer matter alone.
A bigger library, written the way the library is written
idea
small
Today a program can't read its own command line or environment (we
checked: nothing in the language or
std/ names them), the examples hand-write a sort, and
the guide's own Current Limits list text search, trimming, and case
changes as missing. All of it is Vela-shaped work:
include std/sys {args}
include std/slices {sort}
task main()
each a in args
show(a)
fi
fi
-
std/sys: args (the command line as a slice of text),
env(name) at r, exit(n). Small native
tasks, like the files'.
-
std/slices: sort, find, sum,
min, max, over
<T: ordered> and
<T: same> — the generic words already say
all of it.
-
std/text: find, split, trim,
upper, lower, each returning text of the
region it's given, as the guide's own limits page sketches.
-
std/random: a seeded generator with next(r) -> int and
below(n), so tests can be made repeatable.
- What it adds
-
The parts every program outside the examples actually reaches for,
without one new language rule. And since the library is held by
the compiler, every tool gets it at once.
- What it costs
-
Pages in the docs and tests in the suite, and a decision the
project has already learned to make (see
how
std/files was cut): which
parts belong in Vela and which are natives.
Bitwise work as methods
on the table
small
A systems language with no way to and, or, xor, or shift a bit (we
looked; there is none) leaves a gap between what it means to be
— bytes, units, chars — and what it lets a program say.
New symbols would fight the ones already earning their place (&
marks a place, ~ is the error side,
^ points).
Future Directions has already
chosen C's symbols, &, |,
<< and >> (2026-09-23), for
reading as C does, and lists what they must settle (the
>> that closes nested types, the
| error message); this is the case for the other way.
Methods fit the language's own rule, that a member of a value is
written after a dot:
fixed flags = 0b1010
flags = flags.bor(0b0110)
show(flags.band(0b0010), flags.shl(2))
flex c: unit = 0xff
show(c.shr(4), c.bnot())
- What it adds
-
Flags, hashes, packings, and masks, on
int,
unit, and char alike. A shift past the
value's bits stops the program with a message, as arithmetic does;
a result that can't fit (an int shifted left past 63)
stops it too.
- What it costs
-
Six methods on three types, in the builtin list the compiler
already keeps, and the choice of a result type per operation
— the same choice
+ already makes between
int and float. No grammar changes at
all.
An index beside the item: each i, v in xs
idea
small
A map's loop gives a key and its value together; a slice's gives one
item and points at the refuse a program gets today ('each i, x' names a key and its value, which only a map gives). The examples pay the tax everywhere, walking
0..len just to count:
each i in 0..xs.len
when xs[i] > 0
seen++
fi
fi
each i, x in xs
when x > 0
seen++
fi
fi
- What it adds
-
The index beside the item, checked once at the loop's start, the
way a map's loop already works. The error message the compiler
gives today is practically asking for it.
- What it costs
-
One more form of
each, and the rule that
each v in &xs (as it is at each pass) gives the
index too, which it would.
Ranges in case forms: when c is 'a'..'z'
idea
small
match stays on kinds, and the guide refuses a plain
number in a case; but is already opens a
maybe or a variant in a condition, and ranges already
live in slices and loops. Letting a range stand in is,
and in a case of a kind whose payload is an
int or a char, would compress the chains
that grade and classify:
when c is 'a'..'z'
letters++
fi
- What it adds
-
One condition where today a program writes an
else when chain, and one case where it writes
several. The bounds would be constants, as an array's count is.
- What it costs
-
A case form that isn't a single value, which the checker's code
for case forms must carry, and the rule that a range's ends are
constants so the compiler can order them. Exhaustiveness is
untouched: a range never covers a kind's variants by itself, so
the anything case still does its work.
A formatter: vela fmt
on the table
medium
Future Directions lists a formatter among
its tools; this is how it could go. The language is nearly canonical
already: one statement a line, blocks closed with fi,
four-space steps, expressions on one line. Every example, test, and
doc page agrees on one layout — by hand. A formatter would
mostly be a checker: it can't break a line the grammar won't, so its
decisions are few (where the => of an include goes,
how a long shape literal or a long header wraps, whether to keep
else on the when's line, as the docs do).
- What it adds
-
Ends the layout question for every program, forever, and keeps
examples, tests, and the docs' hand-highlighted blocks in step.
Written against the parser the compiler already has, it is the one
tool that gets cheaper as the language stays small.
- What it costs
-
A decision for each form, taken once, and a rule for the few forms
the grammar leaves open today (a long header, a long literal)
— which wants the multi-line forms decided at the same time,
a small piece of future work the guide already points at.
A fuzzer for the invariant that matters
idea
small
The project states its own deepest rule: an internal compiler error
is a report, not a crash, and the lints already deny the panics. The
corpus to break it with already exists: a thousand refused programs
in tests/fail/, each pinned to its exact message. A
coverage-guided fuzzer over front_end_partial and
check_program — whatever bytes go in, a rendered
error or a checked program comes out — would take the
invariant from "the tests say so" to "nothing found a counterexample
today". A nightly job that files each new finding as the next
tests/fail/ program would make the corpus grow itself.
Debug builds with the map back to Vela
on the table
small
The second notebook already proposes
#line directives so C compiler errors and debuggers
point at Vela source. I would take that position and add the other
half: a vela --debug that passes -g to the
C compiler and keeps the generated C in the build dir, so
gdb steps a program and lists the
.vela file it came from. The readable-C design makes
this a short walk: the names in the C are already the names in the
program, and every trap already knows where it is. This is the piece
that turns "readable C" from a comfort into a tool.
Testing in the language: vela test
on the table
medium
Future Directions already lists built-in
testing as an idea, and the shape of it is sitting in this
repository: tests/run/ programs are named, they print
their output, and the harness compares it. The language half is
small from there — a task whose name starts test,
run by vela test beside main, with a
failure rendered the way every other stop is (file, line, caret, and
what was expected). My addition to the existing idea is only this:
the runner should reuse the whole existing harness, because it
already knows how to run programs, feed them input, and diff their
output, and a test runner is mostly that.
A lint for the recursion that looks like a loop
idea
small
The examples walk lists and recurse, as idiomatic Vela does; the
guide says plainly that a deep call chain stops the program with the
system's own crash and no message. C compilers often turn a
self-recursive tail into a jump, but never promise to, and the
checked calls between caller and callee can block the optimization.
A lint that names a task whose every path ends in a send that only
calls the task itself — a tail that reads as a loop but isn't
promised to be one — would point writers at the loop that is
guaranteed, and at
the guide's own open concern, without
adding one rule to the language.
Type macros, the smallest step toward compile time
on the table
small
The second notebook reaches for full
comptime; before that there is a smaller stone to step
on. Today a macro names a value the compiler works out (#PI = 3.14159). Letting one name a type —
#Vec2 = (float, float),
#Grid = [int#64] — would give tuples and arrays
the short names they now lack, with no new grammar: the
# line is already there, and a use site already says
fixed of what type it wants. The one honest cost is
that a name could then be a value or a type, which today it never
is; the rule "a macro is what its declaration says" answers it.
What Kimi adds
GLM read the project for its mechanics: the files, the tests, the
generated C, the places where the code has grown thick. I read it
again for what it could become — the product, the voice, the
bridge from a compiler to an ecosystem. The notes below are mine;
where they overlap with GLM's, I try to push further.
Kimi's lens.
I care about whether a language is adoptable as well as
correct: the first hour, the first bug, the first shared library.
Vela's correctness is already unusual; the question that remains is
how many people will find it before they need something bigger.
The voice is a feature
GLM is right that the prose style is distinctive, but I would go
further: it is a competitive advantage. The docs read as if one
careful person is speaking, and that voice creates trust the way a
polished website never does. Most languages sound like committees;
Vela sounds like a person who has made up their mind. I would
formalize it:
-
A short style guide for Vela's own docs: one idea a
sentence, active voice, no abbreviations, symbols only where they
read as pictures.
-
A lint for new docs pages that flags weasel words,
passive constructions, and sentences over thirty words — the
same way the compiler lints the code.
The result is not vanity. When a reader hits a hard idea like regions,
the clarity of the surrounding prose is what carries them through. The
project has already done the hard part; writing it down as a rule
keeps it.
The test harness is a product
GLM praises the tests/fail/ suite, and it is the right
thing to praise. I would expose it to the user. Today the harness is
internal; a vela test command could treat every program
the same way the project's own suite does:
-
Run
tests/run/*.vela against their
.out files.
-
Run
tests/fail/*.vela and check the first line of each
for the error message.
-
Run
tests/trap/*.vela and check the runtime stop.
-
A
--bless flag that updates the golden files when the
compiler's wording deliberately changes.
This is not a new idea —
Future Directions already lists built-in
testing — but the shape is already finished in
tests/programs.rs. The work is mostly plumbing: read a
tests/ folder beside the user's program and call the same
functions the compiler's own tests call. A user who writes twenty
tests/fail/ cases has a specification of their library's
error contract, not just a test suite.
Literate programs: .vela.md
The project already tests the programs inside
the regions guide. The next step is to make
that relationship general: a file ending in .vela.md is
Markdown whose fenced ```vela blocks are the program, and
the prose between them is the documentation. Running
vela run tutorial.vela.md extracts the blocks in order
and compiles them.
```vela
task main()
show("Hello from the guide")
fi
```
What it adds: tutorials, papers, and notebooks become first-class Vela
artifacts. What it costs: a small extractor and the rule that prose
blocks are comments. It fits the project's obsession with
docs-as-code, and it gives Vela a format no mainstream systems
language has.
Error theater and region-colored editing
Vela Studio already has diagnostics, completion, and hover. The
obvious next step is to make the hard errors visual:
-
Region timelines. When the compiler rejects a
pointer that outlives its region, Studio draws the two blocks: the
region that owns the value and the block that tries to keep it.
-
Aliasing arrows. For the
flex& place-given-twice error, Studio shows the two
expressions that name the same place, with a line between them.
-
Region-colored types. Tint
^T@r,
text@r, and [T]@r with a faint color per
region name in the editor, so a reader sees lifetime boundaries
without reading types.
These are not gimmicks. They are the teaching tools the error messages
already are, extended to the one place a learner actually needs them:
the editor, at the moment of the mistake.
LSP and a web playground
The analysis crate (ide/analysis) already implements
highlighting, completion, hover, diagnostics, and outline. Wrapping it
in the Language Server Protocol would give VS Code, Neovim, and every
other editor the same support Vela Studio has, without asking users to
switch editors. The work is a thin adapter: the analysis crate is the
real engine.
The same crate is compiled to WebAssembly for the Studio web client. A
standalone Vela Playground
— paste code, see errors, run it in the browser — is a
natural sibling. It is the fastest way for a curious programmer to
spend their first hour with the language, and the wasm target is
already built.
Doc comments and vela doc
Today comments start with ; and no comment is a doc
comment. A second form — I would use ;; —
marks text that belongs to the declaration below it:
task countOf<T: same>(xs: [T]@r, wanted: T) -> int
flex seen = 0
each v in xs
when v == wanted
seen++
fi
fi
<- seen
fi
A vela doc command would collect these, the task headers,
the shape fields, and the kind variants, and emit the same dark HTML
the project already writes by hand. The docs' existing style becomes
the generated style. This is a small tool with a large effect: it
makes the library self-documenting the moment std/ grows
past two modules.
Kinds that take types, and closures that capture
These are both already on the table (Future Directions), and I would put them near the front of the queue. They are not
eye-catching in isolation, but they unlock the idioms a reader
expects:
kind Result<T, E>
Ok(value: T)
Err(error: E)
fi
task firstOk<T, E>(xs: [Result<T, E>]@r) -> maybe T
each x in xs
when x is Ok {value}
<- value
fi
fi
<- none
fi
And a lambda that captures a value by copying it into its own region,
as the second notebook sketches, would turn the current "lambda with
no context" into the closures the examples already want (counters,
callbacks, comparators with state). Both fit the existing machinery:
generic kinds are monomorphized the same way generic tasks are, and a
captured closure is a shape with a function pointer and fields.
Named and default arguments
Also already on the table, and the syntax practically designs itself:
Vela already writes every parameter as
name: T, so a default is name: T = value and
a call can name an argument when it wants to skip earlier ones:
task open(path: text, mode: FileMode = FileMode.Read)
...
fi
file.open(mode: FileMode.Write) at r
This is the single most requested feature in almost every young
language, and Vela's parameter syntax is already halfway there. It
would also make the growing library more pleasant:
math.log(x, base: 10.0)
reads better than remembering the order.
Match on numbers and characters
GLM proposed range case forms in is and in cases of a
kind holding an int. I would go one step further and let
match itself work on int and
char, with ranges and a required anything case:
match grade
case 90..100 = "A"
case 80..89 = "B"
case 0..79 = "C"
case _ = "?"
fi
The checker already knows how to insist on a complete
match; ranges just become a compact way to cover
contiguous values, with the same constant-bound rule arrays have. It
is a bigger change than GLM's range case forms, but it is the one that
makes match feel universal rather than kind-specific.
Format strings inside interpolation
Text interpolation is already one of Vela's nicest features. A small
extension to the braces would cover the cases where
.str() is not quite right:
show("{n:04} {x:.2f}" at r)
The cost is a tiny parser inside the string parser and a few runtime
helpers. The benefit is that most everyday formatting needs never
leave the string. For a language that values readable syntax, keeping
the format close to the text is the right place.
Shape layout control
The second notebook proposes controlled
shape layout, and I would add it early because it is small and
C-shaped:
shape Header
packed
magic: unit
version: unit
length: int
fi
A packed keyword (or align N) tells the
compiler to generate a struct with no padding, which is exactly what
file formats and network protocols need. Since Vela already compiles
to C, this is mostly a matter of emitting
__attribute__((packed)) or
_Alignas directives. It would make Vela credible for the
lowest-level parsing work without adding a type system feature.
Package manager as plain files
Future Directions knows Vela needs packages.
I would avoid a central registry for as long as possible and use git
URLs:
name = "mytool"
version = "0.1.0"
[dependencies]
cli = { git = "https://github.com/example/vela-cli", tag = "v1.2.0" }
vela init creates the file;
vela add cli appends a dependency;
vela build
clones missing dependencies into a local cache and treats them as
modules. No registry, no semver solver, no ecosystem politics —
just files and tags, which matches the rest of the project's
minimalism. A central registry can come later, once the community
actually needs it.
Codegen: deduplicate and evaluate early
GLM's interned site table is a good small win. I would add two more,
both visible in the compiler today:
-
Deduplicate generic instantiations. A generic task
monomorphized in two files for the same types currently becomes two
task definitions in the generated C. A global table of instantiated
bodies, keyed by the generic name and the concrete types, would emit
one copy and call it from both places. This is a standard compiler
optimization, but in Vela it also makes the generated C smaller and
faster to compile with the system's C compiler.
-
Fold pure task calls at compile time. A task that
only works on constants and has no side effects (no
show, no new, no flex&)
could be evaluated while checking, the way macros are today. This is
the narrow end of the comptime idea in the second
notebook, and it would let macros grow into real functions without
losing their compile-time power.
What Kimi would do first
GLM's priority list is sound, but my order differs where adoption is
the goal:
-
Kinds that take types and closures that capture.
These unlock the patterns a new user expects; without them, Vela
feels artificially small.
-
vela doc with doc comments. The docs
are already the project's crown; making them generatable keeps that
crown polished as the library grows.
-
LSP adapter and a web playground. The analysis
crate and the wasm build are already there; the gap is packaging.
This is the fastest way to put Vela in front of people who will not
install Studio.
-
std/sys, std/slices,
std/text.
GLM has these right; they make real programs possible.
-
Named/default arguments and match on numbers. Both
remove friction the examples already show.
-
The guard build. GLM puts this first, and it is the
most principled idea; I put it sixth only because I would rather
make the language worth guarding first. Both orders are defensible.