An experimental language for people & machines

Explicit code.
Less time.

A human-readable intermediate representation for AI-generated programs. Precise enough for a language model to write. Explicit enough for a developer to audit.

Read the code Strong types. Native binaries.
No hidden dispatch.

Compile time ↓   Run time ↓
A direction, not a promise of zero. Less waiting. Less overhead. More useful work.

01 / Read what runs

Nothing left
to guess.

Not an opaque intermediate representation. Not shorthand that hides a second language. A compiled Lisp with explicit types, named call arguments, and visible control flow.

Builtins wear #. Invoke generators with #generate; file imports use explicit #alias declarations. Even arithmetic states its type and overflow behavior.

answer.lim Source → native executable
// A function, not a method.
(#fn add ((left #u64) (right #u64)) #u64
  (#return (#u64::add-wrap left right)))

(#fn main () #u64
  (#let answer #u64
    (#call add ((left 40u64) (right 2u64))))
  (#return answer))
Every binding has a type. Every return is explicit.
The addition wraps because the source says so. Download this program ↓

02 / Built for generation. Designed for inspection.

The machine writes it.
You can understand it.

A

Explicit by construction

Strong static types, no type inference. Free-standing functions, no methods, function pointers, or closures. No recursion. Named call arguments show what each value means; explicit operations show what each function does.

B

Concrete all the way down

Full monomorphization through code generation, rather than user-facing generic types. Pure generators produce concrete Lim definitions. Their output is source you can read, search, and review.

(#generate #optional ((type #u64) (name MaybeU64)))
C

Less indirection. More evidence.

Generated source lives in .lim, with call-site provenance. Hash-based caches reuse unchanged generator inputs and dependencies. Inspect the actual implementation, not just a macro invocation.

03 / Names you can follow

One namespace.
No guessing games.

D

Flat, explicit identifiers

Global declarations live in a single namespace. There are no nested namespaces or method lookup rules to reconstruct before following a call. VecU64::sort is a function name, not a method selected through a receiver.

A name such as Work::poll identifies a free-standing function. Choice.Ready identifies an enum variant. Imports explicitly name their source file and declared symbol.

E

Built to be grepped

Search a function or type name to follow its definition and uses. Free-standing functions and no shadowing keep names from silently changing meaning. You can trace the code with a text search, without first understanding a dispatch hierarchy.

Generated definitions are searchable Lim files in .lim, too. Their headers record the invoking files and lines, so you can follow an implementation back to the code that requested it.

F

Keywords that leave your names alone

The entire #-prefixed identifier space is reserved for the language. Keywords, builtin types, and operations are visibly marked: #fn, #u64, #return. Invoke language and standard-library generators with forms such as (#generate #optional ...); invoke your own with (#generate make-record ...).

This supports backward-compatible keyword additions: a future #feature does not turn your existing feature identifier into a keyword. The prefix prevents name collisions; it is not a blanket guarantee against every syntax or semantic change.

04 / Explicit does not mean unchecked

Small surface.
Serious checks.

The compiler follows ownership and borrowing across the program—not just the function in front of you. Explicit source and static checks work together: auditability is not a substitute for safety.

Global borrow checking
Interprocedural analysis tracks returned references and retained borrows without user-written lifetime annotations. Borrowed storage must remain valid, and exclusive access must stay exclusive.
Linear ownership
Values are linear by default. Moving transfers ownership; copying requires explicit support. Linear locals must be consumed before return. Destruction is explicit, not silently inserted.
A visible unsafe boundary
Safe code is checked for memory safety. Raw pointers and unsafe operations have an explicit boundary; unsafe code still needs an audit. Safety checks are not a claim of formal verification.

05 / Time is part of the design

Fast to build.
Native to run.

A dependency-free Rust compiler with its own native instruction encoding and ELF linking. No external assembler or linker in the normal executable build path.

Cached generation avoids repeating unchanged work. Reachability-based emission keeps unused functions out of executable builds. Concrete implementations keep runtime dispatch visible.

Speed is an engineering target, not a universal timing guarantee. Measure your workload with the compiler benchmark harness.

Start with the source

Read. Generate.
Check. Run.

From a checkout of the Lim repository, compile the example above:

nix develop
cargo build --release
./target/release/lim docs/answer.lim -o answer
./answer
# Exit status: 42

Lim is experimental and unreleased. Its syntax and semantics may still change; reserved keyword prefixes are designed to protect user names as the language grows. This is a foundation for explicit, auditable systems—not a finished ecosystem.