What V is: toolchain, hello world, and the C backend · Tag Clone · Seed Pin · Keep the C · Claim Ledger
Clone the tag. Pin the seed. Keep the C. Ledger the claims.
<!-- hal:authoritative:yaml -->
Clone the tag. Pin the seed. Keep the C. Ledger the claims.
§I. Frame
Asr V session 1 of 5, Mon 2026-10-05. Learn-first, contributor-aware, watch-not-adopt. Home: . Every cite is vlang/v at tag 0.5.2 (commit 7647ce1), plus the live vlang.io pages for claim text. The lab ran on the box (Linux x86_64, gcc 14.2.0). Duha #6 tomorrow builds the same tag on the lab Mac and maps the repo tree, so this session stops at the hello program and its C.
§II. What V is, in its own words
docs.md opens with one sentence worth keeping verbatim: V "is a statically typed compiled programming language designed for building maintainable software." It names Go as the nearest relative and lists Oberon, Rust, Swift, Kotlin, and Python as influences.
The next section matters more for us. V has no formal language specification. Until 1.0 the reference is three things: doc/docs.md, the compiler in vlib/v/, and the executable tests under vlib/v/tests/ and friends. The rule is explicit: "When documentation and implementation diverge, compiler behavior and tests are the source of truth." If a lesson this week and the compiler disagree, the compiler wins and the lesson gets a correction.
README adds the maturity line. V is "at an early development stage", changes will land before 1.0, and most syntax changes are handled by vfmt. After 1.0 the plan is a feature freeze. That plan is a statement of intent, so it goes in the claim ledger below.
§III. Four techniques
- Tag Clone.
git clone --depth 1 --branch 0.5.2gives you the release source. The docs showgit clone --depth=1against master; that is a moving target. - Seed Pin.
makebuilds V from a C seed fetched from a second repo,vlang/vc. Pin that seed to the same release or the build breaks. - Keep the C. The default backend emits C and hands it to a C compiler.
-o hello.c,-keepc, and-showcclet you see both halves. - Claim Ledger. Speed, simplicity, and safety lines from the authors are claims. Record them with their source and your own measurement beside them.
§IV. Install, and the pin problem
The documented path is three lines: clone, cd v, make. The GNUmakefile at 0.5.2 shows what make does. It clones https://github.com/vlang/vc into ./vc, runs git pull on it, and fetches a prebuilt tcc from vlang/tccbin into thirdparty/tcc. Then it bootstraps in three steps: the system cc compiles vc/v.c into v1, v1 compiles cmd/v into v2 with -gc none, and v2 compiles cmd/v into the final ./v.
The syllabus asked whether a 0.5.2 build stays pinned. It does not, by default. On the box, after a tag clone, plain make pulled vc at its tip (475a2bd, mirroring a V master commit from 2026-10-04). That newer seed compiled, then rejected the 0.5.2 compiler source with 20 errors, the first in vlib/v/builder/c_error_report.v. make exited 2 after 75 seconds.
The fix is Seed Pin. vc commit messages carry the V commit they were generated from, so git log in vc finds 7eb8c54, titled [v:master] 45ae01d - V 0.5.2. Check that out, then run the Makefile's three bootstrap commands by hand (running make again would pull vc back to the tip). That bootstrap finished in 26 seconds and ./v version printed V 0.5.2 7647ce1. The tcc bundle stayed unpinned (built 2026-07-24, after the tag) and worked; detect_tcc.v reported it healthy.
Two more knobs from docs.md. v up upgrades to the latest V, which leaves the pin, so skip it in a pinned lab. A .vvmrc file holding a version string tells v run to delegate to that compiler version, which is how a project pins itself.
§V. Hello world
fn main() {
println('hello world')
}
v run hello.v compiles, runs, and deletes the executable. v hello.v compiles without running. docs.md also allows dropping fn main() in a single-file program, so println('hello world') alone is a valid script. Both forms printed hello world with exit 0. The docs warn that without an explicit main, every declaration must come before the first statement, because everything after that statement becomes the implicit main.
§VI. The C backend, opened once
README says "V's main backend compiles to human-readable C". docs.md calls the C backend the default (-b c). Three flags make it visible:
-showccprinted the C compiler command. On the box the default compiler was the bundledthirdparty/tcc/tcc.exe, linkinglibgc.a(the Boehm GC) with-D GC_THREADS=1.-o hello.cwrites the C and stops. Our three-line program became 4963 lines (150,617 bytes) of C.-keepckeeps the generated C at a stable temp path. Withv -keepc run hello.v, the executable also stayed in the working directory.
Inside the C, our program is two lines: VV_LOC void main__main(void) calling builtin__println(_S("hello world")). The real int main sets up the GC with GC_INIT() when _VGCBOEHM is defined, calls _vinit, then main__main, then _vcleanup. The other 4900 lines are the builtin module, string constants, and error types. "Human-readable" holds for the part you wrote. The runtime around it is generated code.
§VII. Claim ledger, with our numbers
| Claim | Source | Our measurement (box, 8 cores) |
|---|---|---|
| Learnable in about a weekend | docs.md intro, README, homepage | Not measurable in one session |
| ≈110k loc/s Clang, ≈500k loc/s tcc (i5-7500) | README 0.5.2 | Not measured per line |
| 1.2M lines per CPU per second, no method given | vlang.io/compare (live) | Not measured per line |
| ≈100k Clang, ≈700k tcc/arm64 per second (Apple M5) | vlang.io homepage (live) | Not measured per line |
| Compiles itself in under a second | README 0.5.2; homepage says 0.5 s on M5 | v -o /tmp/v_selftest cmd/v: 4.2 to 4.3 s |
tcc is fast for dev; use clang/gcc for -prod | README C compiler | hello: tcc 0.26 to 0.34 s; gcc 0.52 s; -prod gcc 2.7 to 2.9 s |
The three throughput figures name different hardware, different backends, and only one names a method. They cannot all describe the same thing, so none of them is a number we quote as fact. The self-compile gap ran on different hardware from either author figure; it is our number, not a refutation. Binary sizes for hello: tcc 383,826 bytes, gcc 250,272, -prod gcc 138,456.
§VIII. Lab
- Tag Clone into a scratch dir; confirm
git rev-parse HEADstarts7647ce1. - Run
makeonce and read where it fails, or note that it passed and which vc commit it used. - Seed Pin: check out the vc commit titled
V 0.5.2, run the three bootstrap commands, confirmv version. - Write
hello.vboth ways and run each. v -showcc hello.v, thenv -o hello.c hello.v. Findmain__mainand the Cmain.- Time tcc, gcc, and
-prod. Add your row to the ledger.
Evidence for this fire is in lab/ beside this file: lab.log, timings.txt, hello.v, hello_script.v.
§IX. Common mistakes
- Running
v upin a pinned lab and losing the pin. - Trusting
makeafter a tag clone without checking which vc seed it pulled. - Quoting one of the three compile-speed figures as V's speed.
- Reading
hello.cand calling the whole file "your program". - Treating the post-1.0 freeze plan as a shipped guarantee.
§X. V next to Rust, session 1
Would buy us: a three-command bootstrap from C with no prior V install, sub-second dev builds of small programs through tcc, and generated C we can read and diff. Would not buy us: a formal spec (Rust has a reference and a stability policy; V says the compiler is the spec until 1.0), a reproducible pin by default (the seed floats), or a mature release line. Verdict for today: learn it, keep the ledger, adopt nothing. Duha #6 picks up the lab Mac build and the repo map tomorrow.
Related:
Clone the tag. Pin the seed. Read the C once. Leave the keyboard.