# Tungsten > The Native Language of Agents. > Models write pseudocode. Tungsten runs it. Tungsten is a compact, indentation-based, object-oriented programming language. The self-hosted compiler lowers ordinary code through WIRE to LLVM IR and native binaries. File end in `.w`. The current version is a public preview which targets macOS and Linux; Windows through WSL2. For the comprehensive language and library overview: https://tungsten-lang.org/llms-full.txt ## Start ```sh curl -fsSL https://tungsten-lang.org/install | sh tungsten start --agent tungsten -e '<< 1 + 1' ``` If the installer cannot run in your sandbox (no network to the install host, restricted PATH, etc.), clone the source and bootstrap instead: ```sh git clone https://github.com/tungsten-lang/tungsten cd tungsten bin/bootstrap # builds the self-hosted compiler bin/tungsten -e '<< 1 + 1' ``` From a source checkout: `bin/bootstrap` Note: bootstrapping will depend on a C or Ruby toolchain Run a Tungsten program: ```sh bin/tungsten file.w bin/tungsten file.w -o app && ./app ``` Rebuild the compiler: `bin/tungsten build` ## Read these rules first - Indent with 2 spaces. Dedent closes a block. There is no `end`. - Identifiers are snake_case — uppercase ASCII is not valid in an identifier (`myVar` errors; write `my_var`). PascalCase = classes, SCREAMING_SNAKE = constants. - `<< value` prints. - `+ Name` defines a class; `trait Name` defines a trait. - `-> name(args)` defines a method. - `fn name(args)` defines a method as a pure function and automatically memoizes - No variable declarations: `x = 1` - Like Ruby: the last expression is returned; `return` keyword is optional. - Strings interpolate with `"[expression]"`. - `1.0` is an exact Decimal. `~1.0` is a Float. - Equality follows exactness: Integer/Rational/Decimal cross-equal by value (`2.0 == 2`, `1/2 == 0.5`), but Floats equal only Float values (ordering still crosses: `~2.0 < 3` works). Numeric LITERALS adapt to a Float operand when exactly representable: `~x == 0`, `~x == 0.5`, `case ~x when 2` work; `~x == 0.3` is false (no exact double); variables never adapt. - `≈` is approximate equality across all numerics: `~0.1 + ~0.2 ≈ ~0.3` and `~2.0 ≈ 2` are true (`|a-b| ≤ 1e-12·max(1,|a|,|b|)`); quantities convert units first (`1 km ≈ 1000 m`). Fallback: if the documentation and examples do not settle a syntax form or method name and you must guess, guess Ruby. ## Hello interpolation ``` name = "agent" << "hello [name]" # => hello agent ``` ## Free arg binding ``` (0..9).each -> << i # works (0..9).each -> << x # works too ``` ## Implicit each ``` 10 -> << i # yields 0 through 9 (0..9) -> << i # yields 0 through 9 [1, 2, 3] -> << x * 2 # yields each element in array ``` `value ->` is implicit `each`: an Integer yields `0...value`, a Range yields its elements (`(1..n-1) -> total += 1.0 / (k * k)` works), and a collection yields its elements. The idiomatic short form is a bare `->` with no declared argument name: undeclared lowercase names in the block bind to yielded arguments in first-reference order. Names already bound outside the block are captures. Use explicit parameters (`-> (i)`) only when they make a longer block clearer. `@1`/`@2` are METHOD argument references, not block argument references — do not use them inside `->` blocks. A block declared with multiple params destructures a yielded Array element (Ruby proc semantics): `pairs.each ->(freq, amp)` spreads each `[f, a]` pair, missing params become nil, extras drop. Implicit (param-less) blocks never destructure. ## Map / Reduce ``` list = [1, 2, 3, 4] << list/sq:sum # 30 ``` ## Pipelines In a pipeline, `/message` maps a message over the current values and `:operation` reduces them. Thus `list/sq:sum` squares and sums; `list/count(:prime?)` maps a predicate count across the nested lists above. ``` << (2..100)/prime?:count # 25 << fib(10) # 55 list = [[2, 3, 4], [5, 6, 7]] << list/count(:prime?) # [2, 2] ``` ## Functions ``` fn fib(n) if n <= 1 n else fib(n - 1) + fib(n - 2) ``` ## Numeric identity predicates ```tungsten << 0.zero? # true << 1.one? # true ``` `Number#zero?` and `Number#one?` are the idiomatic identity predicates, especially in generic algebraic code where the value may not be an Integer. Both work on both engine paths. ## Classes ```tungsten + Point -> new(@x, @y) ro -> distance/1 √(Δx² + Δy²) p = Point(3, 4) << p.distance(Point(0, 0)) # 5 ``` Inside a binary method, `x'` means the other object's `x`. An undefined `Δx` is the coordinate difference `x - x'`; after squaring it is equivalent to `(x' - x)²`. ## Control flow and errors ```tungsten << "positive" if score > 0 << "not ready" unless ready? raise "score must be nonnegative" if score < 0 case score when 90..100 grade = "A" when 80..89 grade = "B" else grade = "C" -> risky_operation raise "boom" begin risky_operation() rescue error << "failed: [error]" ensure cleanup() ``` `if` and `unless` may follow a single expression as suffix predicates. `raise value` raises an error; a String is the usual concise form. ## Rich literals The lexer recognizes domain values directly; do not wrap these in parsing constructors unless an API specifically requires one. ```tungsten decimal = 1.0 # exact Decimal float = ~1.0 # binary Float planck = 6.626_070_15e-34 # scientific notation; _ groups digits words = %w[alpha beta gamma] # ["alpha", "beta", "gamma"] symbols = %i[read write execute] # [:read, :write, :execute] samples = %d[1.0 2.5 3.75] # [1, 2.5, 3.75] — exact Decimals buffer = %f64[1.5 2.5 4.0] # typed f64 buffer (raw floats) half = %f32[1.5 2.5] # typed f32 buffer << 0.1 + 0.2 # 0.3, exact Decimal << ~3.14 # binary Float << 3/4 + 1/4 # 1/1, exact Rational << $499.99 - 15% # ≈$424.99, Currency << $3.50 - 25¢ # $3.25 << 5m30s # Duration << 10.0.0.0/8 # IPv4/CIDR << 2001:db8::1 # IPv6; lowercase input << #FF0000 # Color << U+1F600 # 😀 Char << « ff 00 a5 » # ByteArray << 1..10 # inclusive Range ``` The language surface also defines direct Date, DateTime, and UUID literals, for example `2026-07-04`, `2026-07-04T12:30:00Z`, and `550e8400-e29b-41d4-a716-446655440000`; all three evaluate on both engine paths. Dates format with `d.strftime("%Y/%m/%d")` (standard directives), and `d.to_s(fmt)` is its alias; bare `d.to_s` is the canonical ISO form. `%w[...]` is the compact word-array form and `%i[...]` is the corresponding symbol-array form; both evaluate to real Arrays on both engine paths. `%d[...]` is the decimal-array form (a plain Array of exact Decimals), and `%f64[...]` / `%f32[...]` build real typed float buffers — the storage `f64[n]` allocates — ready for elementwise `.+ .- .* ./` operators and numeric kernels. All evaluate on both engine paths. Single-quoted strings are literal — only double-quoted strings interpolate `[expr]`. ## Units of measurement ```tungsten c = 299_792_458 m/s m = 1 kg planck = 6.626_070_15e-34 J·s # scientific notation + compound unit << m·c² # ≈8.988×10¹⁶ J << 3 ft + 12 in # 4 ft << 10 ft * 10 ft # 100 ft² << 9.8 m/s² * 2 s # 19.6 m/s << 1 acre | sqft # 43560 sqft << 6 ft + 2 in | cm(2) # 187.96 cm << 5 m/s | km/h # 18 km/h << 1 J | eV(3) # 6.242×10¹⁸ eV << 2 m + 2 lbs # error: dimension mismatch ``` The conversion-pipe target is a bare unit spelling in any registry form — simple (`km`), mixed-case (`eV`, `mmHg`), compound (`km/h`, `W/m²/Hz`, `kg·m/s`), with superscripts (`W/m²`) — and `(N)` rounds to N digits. Quote the spelling only for names the grammar cannot carry bare (`"metric cup"`, `"cm⁻¹"`). Conversion pipes work inside interpolation, the idiomatic formatting position: `"[speed | km/h(1)]"` renders `18 km/h`. A quantity divided to dimensionless (`q / (1 m)`) passes into `(f64)`-typed native function signatures directly. A pipe on a bare number *attaches* the unit (`2.5 | km` → `2.5 km`), and a pipe on an array maps elementwise — decimals/ints attach, quantities convert — pairing with `%d[…]` for measurement series: ```tungsten samples = %d[1.0 2.5 4.0] | m/s # [1 m/s, 2.5 m/s, 4 m/s] << samples | km/h # [3.6 km/h, 9 km/h, 14.4 km/h] << samples.mean # 2.5 m/s << samples.stdev # sample σ, unit preserved ``` Array statistics — `mean`, `variance` (Bessel-corrected, n−1), `stdev`, `median`, plus `norm` (L2) — are quantity-aware: `mean`/`median` keep the element unit, `variance` carries the squared unit. Quantities also order across units (`1 km > 900 m` is `true`), and `q.value` / `q.unit_name` / `q.to_f` give a quantity's exact Decimal value, unit spelling, and evaluated Float. Math for scientific code: `π` and `τ` are bare constants; superscript powers apply to any value (`π²`, `π⁴`, `x⁷`). `Math.exp`, `log`, `log2`, `log10`, `sqrt`, `cbrt`, `hypot`, `atan2`, the trig/hyperbolic family, and the cancellation-safe `Math.expm1` / `Math.log1p` are all native on both engines. ## π-quantities: exact multiples of π ```tungsten << Math.sin(2π) # 0 — exactly << Math.sin(1000000π) # 0 — exactly, no drift at any magnitude << Math.cos(0.5π) # 0; sin(0.5π) is exactly 1 << 2π * 50 # 100 π — scaling stays exact << 2π + 1 # 7.2831853071795862 — collapses to Float << (2π).to_f # 6.2831853071795862 ``` A number written against `π` (`2π`, `0.5π`, `1000000π`) is a **π-quantity**: an exact decimal multiple of π carried through the unit machinery. Multiplying or dividing by scalars keeps it exact, and `Math.sin` / `cos` / `tan` reduce the exact multiple mod 2 *before* any floating-point rounding — so whole and quarter turns are exactly 0/±1 at any magnitude, and `Math.sin(2π * f * t)` in DSP code never loses precision to a large phase. Evaluation boundaries — mixing with plain numbers, order comparisons, `.to_f`, other `Math.*` calls — collapse it to an imprecise Float. Bare `π` alone is a plain decimal constant (`2 * π` multiplies out immediately). ## Conversions Conversion methods use Ruby-style names: ```tungsten count = "42".to_i # Integer ratio = "42.5".to_f # binary Float whole = 42.9.to_i # 42; truncates toward zero approximation = 42.9.to_f # exact Decimal -> binary Float exact = "42.50".to_d # String -> exact Decimal surface ``` Use `to_d` when the result must remain an exact Decimal, `to_i` for an Integer, and `to_f` for a binary Float. `to_d` also accepts a Decimal (identity) and an Integer (exact conversion); unparseable strings yield Decimal `0`, mirroring `to_i`/`to_f` leniency. ## Exact algebra and geometry ```tungsten use algebra F16 = FiniteField.extension(2, 4) a = F16.generator << F16.minimal_polynomial(a, :x) # x^4 + x + 1 P2 = ProjectiveSpace<ℚ, 2>.new(:X, :Y, :Z) X = P2.coords[0] Y = P2.coords[1] Z = P2.coords[2] C = Curve.new(P2, X**3 + Y**3 - Z**3) C.assert_homogeneous(3) << C.degree # 3 ``` `use algebra` loads finite fields and extensions, polynomial rings, exact factorization, number fields, ideals, projective spaces, curves, divisors, local geometry, elliptic arithmetic, and certificate-oriented computations. ## Scientific computing domains Core ships domain modules that autoload on first reference (no `use` needed, except algebra's literal `use algebra` gate): - `core/algebra` — `Algebra`: exact computation over ℚ and extensions; Sturm real roots, Gröbner bases, Galois theory. Gated by `use algebra`. - `core/calculus` — `Calculus`: forward-AD `derivative` / `gradient` / `jacobian` / `hessian` (Taylor jets, not finite differences), adaptive `integrate`, plus a symbolic layer (series, limits, residues). - `core/physics` — `Physics`: CODATA constants as Quantities (`.boltzmann`) and raw floats (`.boltzmann_si`), ideal gas, compressible Euler systems, finite-volume solvers. - `core/dynamics` — `Dynamics`: flows and maps (Lorenz, Hénon, logistic, …), RK4 + symplectic Verlet/Yoshida integration, fixed points and stability classification, Lyapunov exponents/spectra, Kaplan-Yorke dimension, bifurcation sweeps, Poincaré sections, Takens embedding, correlation dimension. - `core/stats` — `Stats`: descriptive statistics and a seeded RNG (`Stats.rng(seed)` → uniform/normal/exponential/bernoulli); `mean`/`variance`/`stdev`/`median`/`norm` live on Array itself. - `core/optim` — `Optim`: bisection/Newton root-finding, least squares, minimizers; `Solve.ivp` (SciPy-style ODE API) and `Interpolate` (splines, quadrature) sit alongside. - `core/linalg` — `LinAlg`: dense float solve/det/cholesky/qr/eigenvalues over row-major nested lists; BLAS/Accelerate bridges in `core/blas`. - `core/signal` — reserved (stub today); FFT lives in the `FFT` class: `fft`/`ifft`/`rfft`/`abs`/`fft2` on split re/im float arrays. ## Native and GPU code ```tungsten -> dot(xs, ys, n) (f64[] f64[] i64) f64 total = ~0.0 ## f64 i = 0 ## i64 while i < n total += xs[i] * ys[i] i += 1 total ``` ```tungsten ## f32[]: x ## f32[]: y ## i32: n @gpu fn add_one(x, y, n) i = gpu.thread_position_in_grid.x ## i32 if i < n y[i] = x[i] + 1.0 ``` `@gpu fn` is a typed subset, not arbitrary Tungsten on a GPU. The emitter is multi-dialect: Metal (MSL) is always emitted, CUDA C by default, and WGSL when `TUNGSTEN_GPU_DIALECTS` includes it (e.g. `TUNGSTEN_GPU_DIALECTS=cuda,wgsl`). ## Agent tooling ```sh tungsten start --agent tungsten run file.w tungsten build tungsten -c file.w tungsten --ast file.w tungsten --lex file.w tungsten --ll file.w TUNGSTEN_ERROR_FORMAT=json tungsten -c file.w tungsten --explain E_PARSE_UNEXPECTED_TOKEN ``` - MCP server: https://github.com/tungsten-lang/tungsten/blob/main/bits/tungsten-lsp/bin/mcp-server.w - LSP server: https://github.com/tungsten-lang/tungsten/tree/main/bits/tungsten-lsp - Examples: https://github.com/tungsten-lang/tungsten/tree/main/doc/examples - Core library index: https://github.com/tungsten-lang/tungsten/blob/main/doc/CORE.md - Source: https://github.com/tungsten-lang/tungsten Prefer the native compiler for performance, GPU work, and concurrency edge cases. The quick-run and native paths share the everyday surface but are not yet identical in every preview feature. - Use `##` annotations on hot native code and typed buffers. A typed `@gpu fn` subset emits Metal, CUDA, and WGSL kernels; Metal runs natively on supported Apple hardware.