Prismio 0.1.0 is the first release.
Prismio is a self-hosted, statically typed, ahead-of-time compiled language with an inference-driven memory model: you write no free, no lifetimes and no reference counts, and the compiler decides per allocation site which of six implementation tiers a value needs. Every code sample in the documentation is compiled by that compiler as part of the docs build, and the compiler reaches a byte-identical two-generation fixpoint building itself.
Downloads
| Platform | Archive | Needs |
|---|---|---|
| macOS (Apple silicon) | prismio-0.1.0-macos-arm64.tar.gz |
macOS 14 or later for the compiler; programs it builds run on macOS 11 or later |
| Linux (x86-64) | prismio-0.1.0-linux-x64.tar.gz |
glibc 2.34 or later |
| Windows (x86-64) | prismio-0.1.0-windows-x64.zip |
Visual Studio Build Tools and the Windows SDK (see below); no Visual C++ redistributable |
| Linux (ARM64) | prismio-0.1.0-linux-arm64.tar.gz |
glibc 2.34 or later |
| Windows (ARM64) | prismio-0.1.0-windows-arm64.zip |
the same build tools as x86-64 |
Each archive has a .sha256 beside it. The archives are not signed: the checksum shows the download is intact, not who made it, so take both from this release page. On macOS and Linux the installer does this for you and refuses an archive that does not match:
curl -fsSL https://prismio.org/install.sh | sh
On Windows, unpack the .zip and put its bin directory on PATH. The compiler carries its own LLVM but links programs with the system's linker, so you also need the platform's C tools: the Xcode Command Line Tools on macOS, cc and the C library headers on Linux (build-essential on Debian and Ubuntu), or Visual Studio Build Tools with the Desktop development with C++ workload on Windows. The macOS, Linux x86-64 and Windows x86-64 archives are built and tested by the project's CI. The two ARM64 archives are built and tested on virtual machines, so they have had less use. There is no archive for Intel Macs; build from source (see the installation guide).
Highlights
- A smaller compiler. It links three LLVM backends, AArch64, X86 and WebAssembly, instead of all 25, and is about half the size.
--targetaccepts those three families. - Constant array globals. A module-level
let words = ["alpha", "beta"]is read-only data, read by address, and builds nothing at run time. printandprintlntake anything printable,Option<T>included, andstd.termstyles any value that can be printed.- Reading input is shorter:
readLineOr,prompt,readInt,readFloat,readLines,readWordsandisAtEndonstdin. - Faster to compile. The allocation analysis answers its whole-program questions from indexes built once, and the compiler emits its own IR in about half the time (1.4 s to 0.7 s), with identical output.
- A fixed compiler bug. A function that ended in
panic,unreachableorexitwhile owning a value emitted invalid IR; it compiles now.
What is new
Listed against the first release candidate, so a breaking change below is one you only meet if you used it; read the rest as the feature list. What the candidate already had is under Foundations.
Standard library
- Standard input, in the new
std.input:for line in stdin.lines(),stdin.readLine()→Option<String>,stdin.readAll(), and the shorter formsreadLineOr(fallback),prompt(text),readInt()andreadFloat()(anInt?and aFloat?),readLines(),readWords()and theisAtEndproperty.\nand\r\nboth end a line. Counting 3 million lines takes 134 ms, against C++getline's 156 ms and Rustlines()'s 274 ms. std.time:Instant.now()andstart.elapsed()on the monotonic clock,DurationinI64nanoseconds,unixTime()andsleep(duration).- More of the file system:
listDirectory(sorted, without.and..),appendFile,rename(replacing the destination, on Windows too),removeDirectory(empty directories only), andmetadata(path)→Option<Metadata>withsize,modified,isDirectoryandisFile. readLines(path), a file one line at a time, andtryReadLines(path). The reader closes the file itself at the end;close()is for a loop that stops early.StringBuilderinstd.string, for text kept in a struct field:new,append,appendLine,length,isEmpty,toString. 640,000 appends take 73 ms and release everything.Mapmethods and removal:m.get,getOr,set,has,remove,clear,keys(),values(),keyAt,valueAt,length,isEmpty, andm[k], which stops the program on a missing key. Removal is O(1).keyAtreads a key in place when no copy is wanted.OptionandResultmethods:isSome,isNone,isOk,isErras properties;unwrapOrandexpecton both;okOr;ok()anderr(); andmap,andThenandmapErr, whose result type is the closure's.- Environment variables and the process id:
process.env(name)→Option<String>,setEnv,removeEnv,process.pid. std.math: Float roots, rounding, powers, logarithms and trigonometry, integerpow,gcd,lcm,floorDiv,floorModandisqrt, and associated constants such asFloat.PIandInt.MAX. Each Float function lowers to the instruction or C-library call a C compiler would use.- Printing:
print,println,eprintandeprintlnaccept any type with aDisplayimplementation, andOption<T>andString?print their value ornone. They live instd.display, so a program that only prints imports nothing new. std.unicode(grapheme clusters, terminal width, normalization),std.term(terminal colours and styles, applied to anything that can be printed),Process,ChildandStreamfor starting other programs, string interpolation"${x}", andeprint/eprintlnfor every type.
Language
- Conversions:
x as T?is the conversion that can fail and answersnone--300 as U8?,3.5 as Int?,"4x" as Int?-- with no allocation and no branch.x as Stringfor every number,BoolandChar."12" as Intis read at compile time, and"12Sf" as Intis a compile error. - Scalar optionals:
Int?,Float?,Bool?,Char?and an enum'sT?, a flag beside the value that is copied likeTand never allocated -- in a binding, a field, aVec, aMapvalue -- withunwrapOr,expect,==,sortandprintln. - An enum is its own type, and a
matchnaming every variant is exhaustive without_. panic,unreachable,assertandexit, with no import. A failure prints its message and location to stderr and exits with status 101. A function may end inpanic,unreachableorexitinstead of areturn.- Closure bounds:
F: Fn(A, B) -> Rin a parameter list, awhereclause, orimpl Fn(...). A type parameter used only in the result is solved from the closure's return type. A closure that does not fit is refused, naming both signatures. %onFloat, as C'sfmod: the result takes the dividend's sign.- Declared properties:
prop length(self) -> Int, read without parentheses. - Constant array globals: a module-level
letholding an array of literals (let primes = [2, 3, 5, 7]) is read-only data. It cannot be assigned to, and a computed element is refused. - A type's own
Default, thedefaultvalue of any type, type-level functions (Type.fn()), and import groups (import {io, string} from std). - Arrays with a written length (
Array<T, N>), returned by value and stored in struct fields;step,repeat, labelled loops, andforover every collection;extern letfor a foreign global;0xFF,0o755and0b1010literals, and scientific notation in float literals.
Tooling
prismio check --overlay <file> <text>and--module <name>: one file of a program, checked as an editor sees it.- The compiler carries its own LLVM 23 and no longer depends on an installed one, and a Windows program links with MSVC's
link.exe. It contains the AArch64, X86 and WebAssembly backends only, so--targetaccepts those three families and any other triple stops withP1043; see targets. - A diagnostic that finds no overload names the argument types it was given and, for an optional, how to unwrap it; a generic type prints as the program wrote it (
Option<Point>, not an internal name). prismio benchreports in the terminal, and the compiler's own output is styled on a terminal.
Breaking changes
s.length()andx.sqrtno longer compile. A property is declaredpropand read without parentheses; a method takes them.a..bincludesb;a..<bstops before it. A range counts down when its start is past its end:for i in 10..0visits 10 down to 0, where it used to run no times, andstepis a distance.mutcovers what a Vec or an array holds.v.push(x)on a binding that is notlet mutis refused, and a function that changes a Vec it was given takes itinout. A store through a Slice follows the same rule.- A Float prints with at most fifteen significant digits:
println(0.1 + 0.2)prints0.3. - A
matchwhose arms all return ends a function, so the fallbackreturnafter one is now unreachable code. - The old free-function API is gone from programs: the runtime's
list_*entry points, the prefixedstr*andchar*functions (writes.trim(),n.toString()), andstd.fs's rawread_file,join_pathand the other nine, which are nowinternal.List<T>is spelledVec<T>. - Every
parsereturns a scalarT?, notOption<T>:s.parseInt()is anInt?. Writes.parseInt().unwrapOr(0)whereoptionOr(s.parseInt(), 0)was, andx == noneforoptionIsNone(x). It is the same function ass as Int?, and it no longer allocates. - An
Intis not an enum.let c: Color = 1, anIntpassed for aColorand7 as Colorare refused;c as Intandn as Color?are the conversions. PRISMIO_INLINE_ELEMS=0is gone. The run-time switch that stored every list element boxed leaked; flat elements are always stored inline.
Fixed
- No known memory corruption remains. A recursive enum built from
let-bound children no longer double-frees; three ownership shapes that freed memory that was not live — a function returningoptionOr(o, d)for a caller's literal, returning a payload binder, and moving one into a new enum — now keep the value alive instead. Channel<Int>is refused, with a message to send a one-field struct: it used to send a number where the runtime reads a pointer. See channels.- An array as a type argument is refused (
Vec<[Int]>, a function returningBox<[Int]>), because it pointed into a frame the container outlived. See generics. - A task whose thread cannot start is a panic (exit 101, with the reason), instead of running inline, which could deadlock on a channel.
- Results are released where nothing names them: a result passed straight into another call (
println(f(x)),x.trim().toUpper()), and one discarded as a statement. - Floats follow IEEE 754:
NaN != NaNis true,-xkeeps the sign of zero, and a float-to-integerassaturates instead of producing an undefined value. - A function ending in
panic,unreachableorexitwhile it owns a value no longer emits invalid IR, and the sixextern fn exitdeclarations that worked around it are gone. - A C-produced
Stringinside a loop no longer leaks,I64.MAXkeeps its type, andresultIsErrcan be called.
Foundations
- A self-hosted lexer, parser, import resolver, semantic analysis, AIF and LLVM IR generation, with a three-generation bootstrap from a committed, target-neutral LLVM IR seed
- Windows, macOS and Linux, cross-compilation (
--target,--sysroot),-gDWARF debug information, a JIT (--jit), and the UMS package manifest (build.ums) with debug and release profiles, project commands, C sources compiled into a program, a lockfile and local path dependencies - Structs, payload enums,
Option/Result, optionals (T?), arrays,Vec<T>,Slice<T>views andString - Generics by monomorphisation, with per-specialisation container layout; methods and
implblocks; traits, checked statically at the instantiation - Closures: a struct plus a
callfunction resolved by overloading, so no function pointer and no indirect call - Module namespacing and visibility:
public/private/internal, module-qualified calls and selective imports - Concurrency:
spawn/join/Task<R>, and a blocking typedChannel<T>whose receive answersT? - The memory model: six inferred tiers — frame storage, region arenas with automatic placement, scope-bound ownership, non-atomic reference counting, atomic counting where a value provably crosses threads, and a cycle collector.
--verifychecks the inference at run time,--whyexplains one site's tier, and a differential test runs the whole engine against an independent oracle - Programmer-directed AoS↔SoA data views
Not included
async/await, an executor, atomics and other synchronization types, and a specified memory-ordering model. User-written lifetimes, a non-owning typed pointer, exceptions and a ? operator, macros, a package registry and version solving, importing modules from a path dependency, C++ sources and library targets in the manifest, aliased imports, mobile toolchains, and a formatter, linter or language server. Random numbers, calendar dates and time zones, networking, JSON and regular expressions. Collections beyond Vec<T> and Map<K, V> — chunked vectors, VecDeque<T>, sets, a sorted map and a priority queue — are Coming Soon; see what to use until then.
WebAssembly is blocked, not in progress. Prismio emits wasm32 IR, but there is no C library for wasm32-unknown-unknown, so the runtime cannot be built for it.
Performance
On the maintained suite of 62 workloads, each written the same way in Prismio, C++ and Rust, Prismio's geometric-mean time is 0.87× of C++ (clang++ -O3 -flto) and 0.84× of Rust (-C opt-level=3 -C lto=fat), with peak memory level with C++ and 4% below Rust. That was measured on 2026-10-02 on an Apple M5 (arm64, macOS), 5 runs each, with checksum agreement asserted before any timing is read.
Against C++ it is faster on 20 workloads, level on 40 (inside the suite's noise rule) and slower on 2: prime_sieve and large_buffer_copy. Against Rust it is faster on 23, level on 34 and slower on 5: vector_growth, convolution, large_buffer_copy, base64_codec and indirect_calls. Fifteen more workloads are listed in the suite but not implemented yet and are not counted. These are one machine's numbers; prismio bench re-runs the suite on yours.
Known limits
Charis a byte, not a Unicode scalar;std.stringandstd.unicodegive the second, character-level reading of aString.- A heap value is released where it is bound to a name, and most values passed straight on are released too, but some temporary shapes still leak. Each page that has one says so, and
--verifyreports it asleakedwith no violation. - Diagnostics have source spans and recovery, but their codes are not yet stable.
Exact diagnostic prose, AIF report shape, source syntax, runtime ABI and package layout remain pre-1.0 and may change.