Implicit typing
Types, borrows, lifetimes, and error sets are inferred globally when the source stays silent. You write the algorithm; the compiler fills the signatures. Annotations become hard constraints when you add them.
Crisp v1.10.0 · public release
Explicit on demand, implicit by default. Write compact .crp quickly; crisp emits ordinary Rust so the binary is native.
rustc (the soundness boundary)reveal shows the types and borrows you left unwrittenInstall now → Start the tutorial →
cargo install crisp-lang --locked crisp run .
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)))
}
High-performance prototyping: skip type and borrow ceremony while you iterate, then keep a native rustc binary. Annotate when APIs need precision. Emit stays auditable Rust.
Types, borrows, lifetimes, and error sets are inferred globally when the source stays silent. You write the algorithm; the compiler fills the signatures. Annotations become hard constraints when you add them.
Crisp is a front end that produces Rust. There is no VM or GC in the run path. Memory safety and data races remain rustc’s job — not a parallel soundness story.
reveal reconstructs signatures, ownership modes, lifetime overlays, and the error slice a function can produce — so fast writing stays inspectable.
Same programs, 37% fewer GPT-4 tokens than the Rust crisp emits (v1.10.0 examples/, cl100k_base). LLM agents read and generate less, so they iterate quicker. Typical crate as written: 28% fewer (median). Details: llms.txt.
High-performance prototypes in popular Rust domains — CLI tools, services, pipelines — with implicit types and native emit.
Compact snippets from the repo examples — more in the tutorial.
type Color =
| Red
| Green
| Custom(int, int, int)
describe(color) = match color {
Color.Red -> "red"
Color.Custom(r, g, b) -> "rgb({r},{g},{b})"
_ -> "other"
}
type Vec2 = {
x: float
y: float
}
impl Vec2 = {
pub new(x: float, y: float) = Vec2 {
x: x
y: y
}
pub magnitude(self) =
(self.x ** 2.0 + self.y ** 2.0) ** 0.5
}
pub main() = {
v := Vec2.new(3.0, 4.0)
log("mag={v.magnitude()}")
}
trait Show = { show(self) -> str }
type Point = {
x: int
y: int
}
impl Show for Point = {
show(self) = "({self.x},{self.y})"
}
label(x: T) = x.show()
pub main() = {
p := Point { x: 3, y: 4 }
log("p={p.show()} l={label(p)}")
}
shape HasPosition = {
x: float
y: float
}
distance(a: HasPosition, b: HasPosition) -> float = {
dx := a.x - b.x
dy := a.y - b.y
dx * dx + dy * dy
}
type Pair = { left: A, right: B }
id(x: T) = x
first(p: Pair<A, B>) = p.left
shape HasPosition = { x: T, y: T }
distance(a: HasPosition<T>, b: HasPosition<T>) = {
dx := a.x - b.x
dy := a.y - b.y
dx * dx + dy * dy
}
-- crisp.toml: serde_json = { version = "1", rust = true }
use serde_json { from_str, to_string }
pub main() = {
v := from_str("[1, true, \"crisp\"]")
print(to_string(v))
}
parse_port(s) ! = {
if s == "" then throw "empty"
-- …
}
pub main() = {
p := parse_port("8080") catch { e => 0 }
log("port={p}")
}
sum_to(n) = {
total mut:= 0
i mut:= 0
while i < n {
i = i + 1
total = total + i
}
total
}
countdown_stop(start, stop) = {
n mut:= start
loop {
if n == stop then break n
if n <= 0 then break 0
n = n - 1
}
}
pub main() = async {
sleep_ms(1)
print("async-ok")
}
Source → CIR → Rust → native. The emitted crate is an ordinary Cargo project under target/rust/.
.crp → resolve → typeck → ownership → CIR → Rust → rustc → native