Skip to main content

Tantu — a statically-typed, Python-readable language with fibers + channels, compiled to bytecode and run on a hand-built stack VM.

Project description

Tantu logo

Tantu

CI

A statically-typed, Python-readable language with fibers and channels — compiled to bytecode and run on a hand-built stack VM, implemented in Python 3.12 with zero third-party runtime dependencies.

Try it without installing anything: tantu-play.netlify.app has the full guide and an in-browser playground running this interpreter.

  • Design contract: DESIGN.md (v1.2) is the single source of truth.
  • Decisions log: DECISIONS.md records anything the design leaves silent.
  • Status: M1–M9 complete — a full pipeline (lexer → parser → checker → compiler → linker → stack VM), static types with no null (Option/Result), pattern matching, closures, fibers + channels with select, multi-file modules, and user-defined generics. The full test suite and all examples run in CI.

See CHANGELOG.md for release notes and CONTRIBUTING.md if you want to hack on it.

Why Tantu

Every language I like makes me give something up. Python reads like pseudocode but catches nothing before it runs and has no concurrency primitives of its own. Go gives you cheap goroutines, then shares memory by default and only asks you not to race — the detector is opt-in and finds races at runtime, if you're lucky. Rust proves your concurrency is safe but charges you the borrow checker for the proof.

Tantu is a small answer to a narrow question: can a language read like Python, catch your mistakes before it runs, and make data races impossible without a borrow checker?

It gets there with three constraints:

  • No null. Absence and failure are ordinary values (Option, Result), and the checker won't let you ignore them. match and the ? operator are the only ways through.
  • Immutable by default. let bindings and every collection are values; push returns a new list. var buys you a reassignable binding and nothing more.
  • No shared mutable state across fibers. Capturing a var in a closure or a spawned fiber is a compile error. Fibers talk over typed channels, never shared memory.

That third rule is why Tantu exists. A Tantu program can't contain a data race — not because you were careful, but because the language took away the tools to write one. You get Go's concurrency ergonomics (spawn, chan, select) with a guarantee Go doesn't give you, for the price of one checker rule instead of an ownership system.

When to use it, and when not to

Use Tantu if you want a tiny statically-typed language with real concurrency that you can read end to end — for scripts, for exercises, or to see how these pieces fit together. The whole thing is pure Python with no runtime dependencies, and each stage (lexer, parser, checker, compiler, stack VM) is a small module you can read on its own. It's as much something to study as something to run.

Don't use it if you need speed or a library ecosystem. It runs on a bytecode VM written in Python, with a cooperative, deterministic scheduler; it's built for correctness and clarity, not throughput. v1 is deliberately small: erased generics, one concurrency model, one way to handle errors. Need performance? Use Go or Rust. Need libraries? Use Python. Tantu is for when you'd rather have a language you can hold in your head, with the safety properties above spelled out.

A taste

enum Shape:
    Circle(Float)
    Rect(Float, Float)

fn area(s: Shape) -> Float:
    match s:
        Circle(r) => 3.14159 * r * r
        Rect(w, h) => w * h

fn main() -> Unit:
    print(area(Circle(2.0)))
    print(area(Rect(3.0, 4.0)))

No null: absence and failure are values (Option[T], Result[T, E]) unwrapped by match or the ? operator. Bindings are immutable by default (let; var opts in). Concurrency is CSP — lightweight spawned fibers passing messages over typed chan[T] channels, never shared mutable state. See examples/ for runnable programs (hello, arithmetic, closures, collections, options, fib, fizzbuzz, word-frequency, channels, shapes, select, generics).

select waits on several channel operations at once and takes the first ready arm (a recv arm binds Option[T]; a closed channel makes its recv arm fire with None). With no ready arm it blocks until one is; an else arm makes it non-blocking. The canonical use is a timeout — race real work against a fiber that sleeps then signals:

select:
    v = recv(result) =>
        match v:
            Some(n) => print(n)
            None => print("closed")
    deadline = recv(timeout) =>
        print("timed out")

Ready arms are polled in source order, so scheduling stays deterministic and testable.

Install

Tantu needs Python 3.12 or newer and installs a tantu command.

pip install tantu     # from PyPI (or: pipx install tantu)
pip install .         # from a clone

Usage

tantu run file.tn     # run a program (use `run -` to read stdin)
tantu check file.tn   # static check only
tantu dis file.tn     # disassemble to bytecode
tantu                   # REPL
tantu --version

If you're working from a checkout without installing, python -m tantu ... takes the same arguments.

Modules

A program is a set of sibling *.tn files in one directory. import name makes the members of name.tn available as name.member:

# mathlib.tn
fn square(n: Int) -> Int:
    n * n
let answer = 42

# main.tn
import mathlib
fn main() -> Unit:
    print(mathlib.square(5))   # 25
    print(mathlib.answer)      # 42

Running tantu run main.tn discovers the import graph from the root, checks each module in topological order, links every module's globals into one flat table, runs each module's top-level initializers (dependencies first), then calls the root's main(). Import cycles and missing modules are compile-time errors. (import needs a source file on disk — it isn't available for stdin or the REPL.)

Generics

Functions, enums, and structs can take type parameters in [ ]. Generics are fully parametric (no bounds) and erased — a generic definition compiles to a single function or descriptor, and type arguments are always inferred from the call, never written explicitly:

fn map[T, U](xs: List[T], f: (T) -> U) -> List[U]:
    var out: List[U] = []
    for x in xs:
        out = push(out, f(x))
    out

enum Tree[T]:
    Leaf
    Node(Tree[T], T, Tree[T])

struct Pair[A, B]:
    first: A
    second: B

Inside a generic body a type parameter is opaque: it can be passed, stored, matched, and sent over a channel, but not added, compared, or called — there is nothing the checker knows it supports. A generic function is call-only (not a first-class value); pass a lambda with concrete types if you need one as a value.

Development

python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tantu-0.1.0.tar.gz (278.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tantu-0.1.0-py3-none-any.whl (74.5 kB view details)

Uploaded Python 3

File details

Details for the file tantu-0.1.0.tar.gz.

File metadata

  • Download URL: tantu-0.1.0.tar.gz
  • Upload date:
  • Size: 278.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tantu-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d8a26d720dbb51a7acf1ef50631a35aa669f6dfffeda5f4883d73fdb00c5f545
MD5 682812ce3d8bc3b24d115534bf8accbc
BLAKE2b-256 a709b9bcb14c4cc425b36a82de42c2c41633d295434875a6d6644379c1d9c673

See more details on using hashes here.

Provenance

The following attestation bundles were made for tantu-0.1.0.tar.gz:

Publisher: publish.yml on DRACULA1729/tantu-lang

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tantu-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tantu-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 74.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tantu-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ebb570afdeccd11adafb2bc1448d8a0ff9d0867e99e3c0dcc5c159f3a7c7de63
MD5 48ce24013229b2ac25f858bc0cbd14bc
BLAKE2b-256 06aab95e108d5dff53623a7d70a7e521258b9c313739aff6bdbfd321878d2484

See more details on using hashes here.

Provenance

The following attestation bundles were made for tantu-0.1.0-py3-none-any.whl:

Publisher: publish.yml on DRACULA1729/tantu-lang

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page