# Crisp > High-performance prototyping language: compact `.crp` with **implicit types**, inferred ownership, and inferred errors. Compiles to ordinary Rust. The binary is native (`rustc` is the soundness boundary). Write quickly; run at Rust speed. Crisp is for engineers who want to prototype systems code directly — skip type and borrow ceremony while iterating, keep a `rustc` binary when it matters. Same program surface is **37% fewer GPT-4 tokens** than the Rust `crisp` emits, so LLM agents spend less context and generate less. Install: `cargo install crisp-lang`. Spec: v0.2.0-draft. Public release: v1.10.0. Repository: https://github.com/jose-compu/crisp. LLM summary: https://crisp-lang.org/llms.txt ## When to use Crisp - High-performance prototypes that must end as native binaries (CLI tools, services, numerical kernels, FFI wrappers) - Implicit typing: leave types, borrows, and error sets unwritten; annotate public APIs and hot paths - Call existing Rust crates (`rust = true` deps, `extern rust` / `.crpi`) - Fewer tokens than equivalent Rust, so agent loops are cheaper and typically faster ## Compactness vs Rust (token expenditure) Measured 2026-10-15 on **crisp 1.10.0**, 46 `examples/` crates (`abnormal_suite` skipped). Tokenizer: tiktoken `cl100k_base` (GPT-4) and `o200k_base` (GPT-4o). **Same program surface** — `.crp` with `test` / `test_compile_fail` blocks and `--` comments removed, vs emitted `target/rust/src/*.rs` with the duplicated Show/Eq/Ord prelude and `// Generated by crisp` header removed: | Metric | Crisp | Emitted Rust | Crisp more compact | Rust / Crisp | |---|---:|---:|---:|---:| | cl100k tokens | 10 415 | 16 638 | **37%** | 1.60× | | o200k tokens | 10 485 | 16 724 | **37%** | 1.60× | | bytes | 31 929 | 53 341 | **40%** | 1.67× | **Typical crate as written** (raw files, tests and comments kept): median **28%** fewer cl100k tokens (35 of 46 crates smaller). Corpus-weighted raw is 12% because large test files keep tests in `.crp` that `crisp emit` does not duplicate. Implication: an agent that authors `.crp` instead of the emitted Rust spends ~1.6× fewer prompt and completion tokens on the program itself. That leaves more context for tools, specs, and iteration. ## When not to use Crisp - Need a GC, interpreter, or scripting VM — Crisp has none; emit is Rust - Need `dyn Trait` (typeck **E0091**) or full generic trait bounds - Need self-hosting (Rust-hosted bootstrap only; self-host → v2.0.0) - Treat the draft spec as fully implemented — see [limitations](https://crisp-lang.org/limitations.html) v1.10.0: `xs[lo..hi]` copies to `vec` (#159); `extern rust` `vec` (`&[String]`) / `vec` (#158); nested `vec>` parses (`>>` is two `>`); trait impl set **E0092**, `dyn Trait` **E0091** (#59). v1.9.0: `extern rust` `vec` / `vec` (#153); OS prelude `env_*` / `path_*` / `fs_write` (#151); `src/lib.crp` emits Cargo `[lib]` (#152). v1.8.1: `as float + expr` (#140); unbraced `else` newline (#145); parenthesized index emit (#142); `let mut` / `mut` params for index writes (#141); harness `.len()` (#144); nested-module tests (#143); module-qualified fn stubs (#146). v1.8.0: `else if` (#117); optional record commas (#111); parse/lex `file:line:col` (#109); unary `-` on float (#113); checking-position `int`→`float` / `as float` (#112); Copy records (#118); harness CIR args (#114); implicit `vec` and `xs[i]` (#119 / #120); prelude `exp`/`sin`/`cos`/`tanh`/`sqrt` (#115); `extern rust` scalars (#116). v1.7.3: `crisp.toml` path deps (`foo = { path = "…" }`, #105, `examples/path_dep`); `crisp run` cwd is the crate root, not `target/rust` (`CRISP_CRATE_ROOT`, #106). v1.7.2 compiler fixes: binop grouping (`(lo + hi) / 2`, #99), nested type `use` (`crate::…::Verdict`, #100), string `match` arms (#101), `crisp test` unique names / bool-str `assert_eq!` (#102). ## Docs - [Home](https://crisp-lang.org/): High-performance prototyping positioning - [Install](https://crisp-lang.org/install.html): Install `crisp-lang` / `crisp-lsp` from crates.io or from source - [Tutorial](https://crisp-lang.org/tutorial.html): Guided introduction to Crisp syntax and toolchain - [Learning path](https://crisp-lang.org/learning.html): Ordered path through docs and examples - [Language](https://crisp-lang.org/language.html): Language surface — implicit types, bindings, generics, errors, modules - [Docs hub](https://crisp-lang.org/docs.html): Spec, limitations, and documentation index - [CLI](https://crisp-lang.org/cli.html): `crisp` and `reveal` command reference - [Use cases](https://crisp-lang.org/use-cases.html): CLI tools, services, pipelines — native emit, compact source - [Limitations](https://crisp-lang.org/limitations.html): Honest known gaps vs the draft spec ## Install (agents) ```bash cargo install crisp-lang --locked crisp --version crisp run . ``` Requires Rust 1.85+ (`rustup`). Optional LSP: `cargo install crisp-lsp --locked`. ## Hello world ``` shape Named = { name: str } type Guest = { name: str = "world" } id(x: T) = x greet(who: Named) = "hello {who.name}" pub main() = { world := Guest {} print(id(greet(world))) } ``` `crisp run examples/hello` prints `"hello world"`. Shape `Named` is structural; unbound `T` is a type parameter (implicit typing); `Guest {}` uses the field default. No type annotations on `main`. ## Generics (prefer implicit binders) Unbound names in type position are parameters. Write `<>` to pin a definition or to apply arguments: ``` type Pair = { left: A, right: B } id(x: T) = x first(p: Pair) = p.left shape Boxy = { value: T } unwrap_int(b: Boxy) = b.value trait Wrapper = { unwrap(self) -> T } impl Wrapper for IntBox = { unwrap(self) = self.value } ``` Keep `T`. The body infers the constraint (`+` → `T: Add`; `x.show()` → `T: Show`). Unsatisfied instantiations are typeck **E0084**. ``` shape HasPosition = { x: T, y: T } distance(a: HasPosition, b: HasPosition) = { dx := a.x - b.x dy := a.y - b.y dx * dx + dy * dy } label(x: T) = x.show() ``` Pins (same meaning): `id(x: T)`, `type Pair`. Examples: `examples/generics_implicit` (preferred), `examples/generics`, `examples/generics_pub`, `examples/shapes_generic`, `examples/shapes_user`, `examples/show_trait`. Nested `vec>` parses. `dyn Trait` is **E0091**; leftover generic trait bounds remain. ## Function values There is one callable kind. `double(n) = n * 2`, `inc := |n| n + 1`, and passing `double` to `apply(f, x) = f(x)` are the same values. ``` apply(f, x) = f(x) run(f) = f(21) label(g, p) = g(p) type Person = { name: str } pub main() = { print(apply(double, 21)) print(apply(_ * 2, 21)) print(run { |x| x * 2 }) print(label(.name, Person { name: "Ada" })) print(apply(.magnitude(), v)) } ``` Sugar when a function is expected: holes (`apply(_ * 2, n)`), trailing last-arg (`run { |x| x * 2 }`), field sections (`label(.name, person)`), method sections (`apply(.magnitude(), v)`; extra args bake in: `.scale(2.0)` → `|v| v.scale(2.0)`). Example: `examples/closures`. Too many/few holes is E0085; `_` where a function is not expected is E0086. Operator sections (`+ 1`) and `dyn Fn` are not shipped. ## Source & package - GitHub: https://github.com/jose-compu/crisp - crates.io package: https://crates.io/crates/crisp-lang (bins: `crisp`, `reveal`) - Spec (repo): https://github.com/jose-compu/crisp/blob/main/docs/spec/CrispLang-SPECS-0.2.0.md - Changelog: https://github.com/jose-compu/crisp/blob/main/CHANGELOG.md ## Optional - [Full site map](https://crisp-lang.org/sitemap.xml) - [robots.txt](https://crisp-lang.org/robots.txt)