Design Decisions & Trade-offs¶
Philosophy¶
Prove is an intent-first language. Every function declares its intent — a verb names the purpose, contracts state the guarantees, and explain documents the reasoning — before a single line of implementation is written. The compiler then enforces that the implementation matches the declared intent.
The programmer remains the author — the compiler is a deterministic assistant that enforces what you declared. Intent comes first: the verb names the purpose, contracts state the guarantees, explain documents the reasoning. The type system serves this intent enforcement, moving correctness checks from runtime to compile time and generating tests from the code you already write. Most bugs are type errors in disguise — give the type system enough power and they become almost impossible.
Implementation Decisions¶
File Extension: .prv¶
Investigated .pv, .prove, .prf, .pr, and .prv. Chosen: .prv — short, reads naturally as "Prove", and has no conflicts with existing programming languages or developer tooling.
| Rejected | Reason |
|---|---|
.pv | Taken by ProVerif (formal methods — same domain, high confusion risk) |
.prove | Taken by Perl's prove test harness (well-known in dev tooling) |
.prf | Taken by MS Outlook profiles and Qt feature files |
.pr | Legacy Source Insight 3, but "PR" universally means "pull request" |
Prototype Implementation: Python¶
The compiler POC is implemented in Python (>=3.11). The goal is to validate the language design and prove out the compilation pipeline before rewriting in a systems language.
Compilation Target: Native Code¶
As close to the CPU as possible. The compiler does the heavy lifting at compile time so the output is fast and memory-efficient. Target: native code via C emission compiled by gcc/clang. An earlier ASM backend (x86_64) was prototyped at v0.2 and archived — C emission provides better portability and optimization. No VM, no interpreter for production output.
First POC: Self-Hosting Compiler¶
The first program written in Prove will be the Prove compiler itself. The bootstrap path: (1) write a complete compiler in Python, (2) use it to compile a Prove compiler written in Prove. This exercises the type system (AST node types, token variants), verb system (transforms for pure passes, inputs for file reading, outputs for code emission), pattern matching (exhaustive over AST nodes), and algebraic types — proving the language works by compiling itself. Self-hosting is the strongest possible validation: if Prove can express its own compiler, it can express anything.
AI-Resistance: Fundamental¶
AI-resistance features (implementation explanations, intent declarations, narrative coherence, context-dependent syntax, semantic commits) are mandatory and fundamental to the language identity, not optional extras. requires and ensures are hard rules the compiler enforces automatically. explain documents the chain of operations in the implementation using controlled natural language — with ensures present, references are verified against contracts and row count must match the from block.
Comptime: IO Allowed¶
Compile-time computation (comptime) allows IO operations. This enables reading config files, schema definitions, and static assets at compile time. Files accessed during comptime become build dependencies — changing them triggers recompilation. This may be revisited if reproducibility concerns arise.
CLI-First Toolchain: prove¶
The prove CLI is the central interface for all development:
prove build # compile the project (mutation testing runs by default)
prove build --debug # compile with debug symbols
prove build --no-mutate # compile without mutation testing
prove test # run auto-generated + manual tests
prove check # type-check without building
prove format # auto-format source code
prove format --status # check formatting without modifying
prove lsp # start the language server
prove new <name> # scaffold a new project
Syntax Philosophy¶
No shorthands. No abbreviations. Full words everywhere. The language reads like English prose where possible. Since it is inherently a hard-to-learn language (refinement types, implementation explanations, effect tracking), simplicity is maximized wherever possible. If something can be simple, it must be. The compiler works for the programmer, not the other way around.
Secondary Priorities (Deferred)¶
- Calling Prove from other languages — deferred until the FFI story is more mature.
- Method syntax — deferred. All function calls use
function(args)form. Noobject.method()dot-call syntax. Keeps the language simple and avoids dispatch complexity. Field access (user.name) is unaffected.
Concurrency — Structured Concurrency¶
Prove provides structured concurrency through the async verb family (detached, attached, listens) backed by stackful coroutines (prove_coro). Because pure verbs (transforms, validates, reads, creates, matches) guarantee no shared mutable state, the compiler enforces safe concurrency boundaries.
Thread-based par_map, par_filter, and par_reduce are available for pure verbs, running a pthreads-backed thread pool with automatic core detection. They are restricted to pure verbs at compile time — IO and async verbs are rejected. See Parallel Iteration.
The verb system enforces purity boundaries — pure verbs cannot make IO calls, and the compiler rejects violations at compile time.
Error Handling — Errors Are Values¶
No exceptions. Every failure path is visible in the type signature. Uses ! for error propagation. Panics exist only for violated assume: assertions at system boundaries — normal error handling is always through Result values.
main()!
from
config as Config = read_config("app.yaml")!
db as Store = connect(config.db_url)!
serve(config.port, db)!
Zero-Cost Abstractions¶
- Pure functions auto-memoized and inlined
- Region-based memory runtime; per-function scoping is upcoming
- Use-after-move detection for
Ownmodifier; comprehensive tracking is upcoming - No GC pauses, predictable performance
- Native code output
Pain Point Comparison¶
| Pain in existing languages | How Prove solves it |
|---|---|
| Tests are separate from code | Testing is part of the definition — ensures, requires, near_miss |
| "Works on my machine" | Verb system makes IO explicit (inputs/outputs) |
| Null/nil crashes | No null — use Option<Value> for absent values, Unit for no return value |
| Race conditions | Ownership + verb purity; structured concurrency via detached/attached/listens |
| "I forgot an edge case" | Compiler generates edge cases from refinement types |
| Slow test suites | Property tests generated from contracts |
| Runtime type errors | Refinement types catch invalid values at compile time |
Trade-offs¶
An honest assessment of the costs:
- Compilation speed — Proving properties is expensive. Incremental compilation and caching are essential. Expect Rust-like compile times, not Go-like.
- Learning curve — Refinement types, verb purity, and formal contracts are unfamiliar to most developers. The compiler's suggestions help, but there's still a ramp-up.
- Ecosystem bootstrap — A new language needs libraries. A C FFI and a story for wrapping existing libraries is a secondary priority, deferred until the core language is stable.
- Not every property is provable — For complex invariants the compiler falls back to runtime property tests, which is still better than nothing but not a proof.
The core bet: Making the compiler do more work upfront saves orders of magnitude more time than writing and maintaining tests by hand.
AI Transparency¶
Prove is built with honesty about where AI tools are and aren't used.
What is human-authored¶
The Prove language itself — its syntax, semantics, type system, intent verbs, contract model, explain verification, AI-resistance mechanisms, refinement types, and every other novel design idea — is entirely human-invented. All .prv source code (including the planned self-hosted compiler) is written by humans.
Where AI tools help¶
AI tools have been used as implementation aids for the surrounding tooling:
- Bootstrap compiler — the Python CLI, checker, emitter, and supporting infrastructure
- C runtime — the stdlib implementations backing binary functions
- Documentation — writing and maintaining these docs
- Editor integration — tree-sitter grammar, Pygments lexer, Chroma lexer
No single AI tool is credited. Multiple LLMs and open source models have been used throughout development as conceptual partners and coding assistants.
After self-hosting¶
Once the compiler is self-hosted (V2.0, written in Prove and compiled by the V1.0 bootstrap), AI involvement will be limited to documentation maintenance and conceptual discussion. The self-hosted compiler will be human-authored Prove code.
Licensing reflects this¶
The language and .prv source code are covered by the Prove Source License v1.0, which prohibits use as AI training data. The AI-assisted tooling (bootstrap compiler, docs, lexers) is licensed under Apache-2.0. This separation ensures legal clarity: the parts built with AI help use a license compatible with that workflow, while the human-authored language retains its protections.
Inspirations¶
Prove draws from many languages but combines their ideas into something none of them offer: intent-first programming, where every function declares its purpose, guarantees, and reasoning before the implementation begins. The languages below shaped individual features — the verb system, the contracts, the type safety — but the synthesis is uniquely Prove's.
| Language | What Prove borrows | What Prove avoids |
|---|---|---|
| Rust | Ownership model, exhaustive matching, no null | Lifetime annotation burden, borrow checker complexity |
| Haskell | Type system, pure functions, algebraic types | IO monad complexity, lazy evaluation surprises |
| Go | Parameter syntax (name Type), simplicity as goal | Weak type system, error handling verbosity |
| Python | Indentation-based blocks, readability philosophy | Dynamic typing, runtime errors |
| Zig | comptime (compile-time computation with IO) | Manual memory management |
| Ada/SPARK | Contract-based programming, formal verification | Verbose syntax |
| Idris/Agda | Dependent types for encoding invariants | Academic accessibility barrier |
| Elm | Eliminating runtime exceptions, compiler as assistant | Limited to frontend |
| F# | Pragmatic algebraic types, pipeline operator | — |
Contributing¶
The Gitea instance at code.botwork.se/Botwork/prove is a paid service for issue creators.
Developers who want contributor access can reach out to magnusknutas[at]botwork.se.
Development Setup¶
To set up your development environment, run ./scripts/dev-setup.sh from the workspace root to install all dependencies. If a tool or package is missing during development, run the script first. If it's still missing after running the script, add the dependency to dev-setup.sh.