Bit is a systems programming language with TypeScript-flavored syntax and
Go-like semantics. You write let/const, fn, arrows, interface,
and <> generics; you get garbage collection, green threads (spawn), typed
channels, and structural interfaces. Programs compile to a single static native
binary with zero runtime and zero external toolchain - the compiler owns every
stage from lexer to linker, so there is no LLVM, no system assembler, and no
libc dependency.
Pre-1.0. The compiler is self-hosted - written in Bit, compiling itself to a fixed point (the binary stage 2 produces and the binary stage 3 produces are byte-identical). The language, standard library and toolchain work: package manager, language server, formatter, linter, test runner.
What that does not mean: the API is not frozen, and 0.x releases carry no
support guarantee. Read the support policy before
depending on it.
Platforms. Linux and macOS on x86-64 and ARM64, and Windows on x86-64. ARM64 Windows is out of scope for the Windows port; x86-64 is the only Windows target.
brew install byteink/tap/bit # macOS
curl -fsSL bitlang.org/install.sh | sh # Linux
irm bitlang.org/install.ps1 | iex # Windows
Any of them gives you a single static binary with nothing else to install - no runtime, no VM, no libc dependency. Check it:
bit --version
Or run the toolchain as a container, no install at all:
docker run --rm -v "$PWD:/work" ghcr.io/byteink/bit run hello.bit
# to BUILD into your project, pass your own uid so the output belongs to you:
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/work" ghcr.io/byteink/bit build hello.bit
The image runs as an unprivileged user, so without --user it can read your
sources but not write a binary back into them.
New here? Get started takes about fifteen minutes and ends with a real concurrent program.
You only need this to work on Bit. Users install the binary above.
No toolchain to install. ./make downloads and digest-verifies the pinned
previous release and builds this tree with it - the usual chicken-and-egg every
self-hosted language has, resolved by a published binary rather than by a second
compiler. You need sh, curl and tar; none of it is needed to use Bit.
./make # build the compiler into bit-out/bin
./make test # run the full suite
./make --list # every step and what it does
scripts/gate.sh # run only what your diff can affect
docs/development.md covers the bootstrap chain, the
testing conventions, the 800-line file-size rule and the traps a green build does
not catch. Read it before a large refactor.
| Path | Purpose |
|---|---|
compiler/ |
The compiler (bit), written in Bit |
runtime/ |
Runtime linked into user binaries, written in Bit |
stdlib/ |
Standard library, written in Bit |
spec/ |
Language specification (source of truth) |
docs/ |
Reference and tutorial documentation |
editors/ |
Editor support (VS Code extension, LSP client) |
dist/ |
Packaging (brew formula, installers) |
_tests_/ |
Golden-file and stress tests |
See docs/release/VERSIONING.md for what
counts as a breaking, additive, or fix change across the language, CLI,
stdlib, and runtime ABI.
Bit compiles to a single static native binary with a garbage-collected runtime.
The tables below compare four CPU-bound micro-benchmarks against Go (also GC'd)
and C (-O2), covering call overhead, float math, integer/branch work, and
allocation churn. Reproduce with bench/run.sh; sources live in bench/cases/.
Full method and caveats below the tables.
| Benchmark | Bit | Go | C | Bit / Go | Bit / C |
|---|---|---|---|---|---|
| fib | 1012.0 M | 945.3 M | 635.8 M | 1.07x | 1.59x |
| mandelbrot | 2133.6 M | 1639.1 M | 1606.1 M | 1.30x | 1.33x |
| collatz | 917.2 M | 626.2 M | 411.4 M | 1.46x | 2.23x |
| alloc | 1149.6 M | 392.7 M | 641.1 M | 2.93x | 1.79x |
| allocflat | 248.5 M | 119.9 M | 17.7 M | 2.07x | 14.05x |
| strings | 1066.9 M | 373.7 M | 109.2 M | 2.86x | 9.77x |
| map | 323.7 M | 389.2 M | 172.7 M | 0.83x | 1.87x |
| strmap | 1343.5 M | 676.4 M | 255.2 M | 1.99x | 5.26x |
| sort | 315.5 M | 356.9 M | 285.4 M | 0.88x | 1.11x |
| matrix | 1382.3 M | 951.7 M | 397.5 M | 1.45x | 3.48x |
| json | 1565.2 M | 340.1 M | 230.5 M | 4.60x | 6.79x |
| Benchmark | Bit | Go | C |
|---|---|---|---|
| fib | 5962.3 M | 5136.6 M | 3146.6 M |
| mandelbrot | 4793.0 M | 2506.1 M | 2698.8 M |
| collatz | 2578.8 M | 935.1 M | 930.5 M |
| alloc | 7997.8 M | 2154.2 M | 4289.7 M |
| allocflat | 1645.5 M | 401.9 M | 59.3 M |
| strings | 7455.2 M | 1714.6 M | 643.3 M |
| map | 793.6 M | 693.0 M | 229.0 M |
| strmap | 6649.6 M | 2218.0 M | 539.8 M |
| sort | 1928.7 M | 942.6 M | 508.7 M |
| matrix | 12279.7 M | 7323.5 M | 1656.2 M |
| json | 10576.4 M | 2139.3 M | 1371.6 M |
| Benchmark | Bit | Go | C |
|---|---|---|---|
| fib | 0.240s | 0.220s | 0.140s |
| mandelbrot | 0.480s | 0.370s | 0.360s |
| collatz | 0.200s | 0.140s | 0.090s |
| alloc | 0.260s | 0.070s | 0.150s |
| allocflat | 0.050s | 0.010s | 0.000s |
| strings | 0.240s | 0.050s | 0.020s |
| map | 0.070s | 0.080s | 0.040s |
| strmap | 0.310s | 0.150s | 0.060s |
| sort | 0.070s | 0.080s | 0.060s |
| matrix | 0.310s | 0.210s | 0.090s |
| json | 0.360s | 0.070s | 0.050s |
| Benchmark | Bit | Go | C |
|---|---|---|---|
| fib | 1.9 MB | 4.2 MB | 1.4 MB |
| mandelbrot | 1.9 MB | 4.2 MB | 1.4 MB |
| collatz | 2.0 MB | 4.2 MB | 1.4 MB |
| alloc | 6.1 MB | 10.5 MB | 1.6 MB |
| allocflat | 5.7 MB | 11.3 MB | 1.5 MB |
| strings | 134.2 MB | 69.5 MB | 30.7 MB |
| map | 36.3 MB | 41.5 MB | 193.5 MB |
| strmap | 35.6 MB | 30.8 MB | 40.8 MB |
| sort | 25.1 MB | 14.8 MB | 11.0 MB |
| matrix | 8.2 MB | 11.0 MB | 7.5 MB |
| json | 179.0 MB | 57.6 MB | 125.2 MB |
| Benchmark | Bit | Go | C |
|---|---|---|---|
| fib | 6 | 195 | 0 |
| mandelbrot | 6 | 195 | 0 |
| collatz | 6 | 195 | 0 |
| alloc | 10006006 | 10002306 | 10004000 |
| allocflat | 4006 | 2303 | 2000 |
| strings | 7125035 | 338 | 11 |
| map | 14 | 4378 | 1 |
| strmap | 400041 | 400797 | 400003 |
| sort | 600022 | 150285 | 150002 |
| matrix | 16 | 284 | 3 |
| json | 5400061 | 1049989 | 2250021 |
| Benchmark | Bit | Go | C |
|---|---|---|---|
| fib | 234 KB | 2373 KB | 33 KB |
| mandelbrot | 234 KB | 2373 KB | 33 KB |
| collatz | 234 KB | 2373 KB | 33 KB |
| alloc | 234 KB | 2390 KB | 33 KB |
| allocflat | 234 KB | 2373 KB | 33 KB |
| strings | 252 KB | 2389 KB | 33 KB |
| map | 272 KB | 2390 KB | 33 KB |
| strmap | 291 KB | 2390 KB | 33 KB |
| sort | 269 KB | 2407 KB | 33 KB |
| matrix | 234 KB | 2373 KB | 33 KB |
| json | 343 KB | 3679 KB | 33 KB |
| Metric | Bit | Go | C |
|---|---|---|---|
| Process startup (per exec) | 4.143 ms | 4.278 ms | 3.682 ms |
Bit compile speed: 605 lines/sec (669 lines across 11 cases, warm).
Machine: Apple M5 Max, macOS 26.6.2. Bit @
7531627a, Go go1.27.1, Apple clang version 21.0.0 (clang-2100.1.1.101). Method: 15 runs per case per language. Cycles and instructions are a trimmed mean of those runs, meaning the mean after dropping the slowest fifth, which was the most reproducible of four estimators measured over 40 samples per series; wall clock and RSS are the median. C builtcc -O2 -ffp-contract=off, Gogo build, Bitbit build, each language's standard optimized build. Mandelbrot: Bit and C agree to the last bit; Go differs by ~0.0002% because it contractsa*b+cto a hardware FMA. Not a bug: cross-compiler float bit-identity is not guaranteed. alloc measures the ALLOCATOR: 10M short-lived nodes, each its own heap object in all three languages (Bit's element class has a reference field, Go holds[]*Node, C mallocs per node). allocflat measures DATA LAYOUT: the same 10M nodes and the same printed total, stored by value in one buffer per batch (Bit packs[]Nodeinline since #3862, Go holds[]Node, C mallocs the batch once). The gap between the two rows is what per-node heap allocation costs a language. The allocation table above is how those two claims are checked rather than asserted: same order of magnitude across a row means the three sources still express the same data structure, which is exactly whatallocsilently lost for a day (#3934). Bit's count isswept+livefromBIT_GC_STATS=1; Go's isruntime.MemStats.Mallocsand C's amalloccounter, both opt-in (BENCH_ALLOC_STATS,-DBENCH_ALLOC_STATS) and both absent from every timed binary. The ratios are built from CYCLES, not from wall clock./usr/bin/timereportsrealin hundredths of a second and most of the C sides here finish in under 0.10s, so a wall-clock ratio for those rows is quantisation:mappublished 7.50x C off 0.300s/0.040s where the counters say ~4.5x. Adding runs does not fix that, because it narrows the spread around a quantised value instead of removing the quantisation, so the unit changed (#4040). Both counters come from the same/usr/bin/time -linvocation that already produced the wall clock and the RSS; nothing extra is run and nothing extra is installed. The wall-clock table is kept as context and carries no ratio column. Cycles and instructions are startup-corrected: each figure has that language's own empty-program cost (bench/cases/startup, bit 4.5M, c 3.8M, go 5.6M) subtracted, because dyld and runtime init differ per language and are a fifth of C'sallocflatrow. Every other table is raw.
On
matrix, read the Go column as the target and not the C one. The C side vectorises: it retires 2.06 instructions per inner-loop iteration against Go's 9.09, so the Bit:C ratio on this row compares a scalar loop against a vectorised one and is not a statement about codegen quality. A scalarcc -O2 -fno-vectorizecontrol build of the same case retires 8.07, which is the like-for-like figure. Denominator for all three:trials * n^3 = 6 * 512^3 = 805,306,368inner iterations. Reproducibility was measured rather than assumed (#4040): four independent regenerations of this table on this box held every ratio to 2.5% between adjacent runs and 8.5% at worst across all four. The loose rows arealloc,map,allocflatandstrings, whose Go or C side is short enough that that language's own allocator and collector scheduling moves it by several percent from run to run;matrix,mandelbrot,fibandsortreproduce to about 1%. On those four loose rows, read a change under ~3% as noise. Peak RSS above is a within-run median like every other figure in that table, but it can still swing further ACROSS separate regenerations than one run shows (#4199). From every regeneration recorded inbench/history.csv, restricted to the same four loose rows above and to the Go/C columns (the Bit column reflects real compiler/runtime changes over that history, not noise):allocc: 1.5-1.7 MB (median 1.6 MB, N=18);stringsgo: 64.3-90.9 MB (median 69.0 MB, N=15). Read that cell's published number as representative of the stated range, not a fixed constant. Instructions are published beside cycles because a cycle gap alone does not say whether it is work emitted or work stalled, and the two ratios differ a lot here: Bit retires roughly 4-7 instructions per cycle against C's 1.4-1.8, so its instruction ratio always overstates its cycle ratio. Cycles are the time; instructions are the reason. Generated bybench/run.shon 2026-09-09T10:00:52Z. Do not edit by hand.
Bit is licensed under the Apache License 2.0.
See CONTRIBUTING.md for contribution guidelines, SECURITY.md for reporting vulnerabilities, and TRADEMARK.md for trademark guidelines.