(tulam)

One high-level language for mathematics, applications, objects, and heterogeneous execution.

Proper Pi and Sigma types, algebraic evidence, first-class classes, deep effects, explicit call-by-need, and target-aware optimization without call-site ceremony.

Start the User Guide Language Reference Download Syntax Highlighter Build tulam
math.tl
// Library authors state optional placement once.
value bulkCompute : TargetPolicy =
    prefer(capability(ParallelBulk), current);

function sumOfSquares(xs:c(a)) : a
    requires Field(a), Bulk(c)
    on bulkCompute =
    fold((+), zero, fmap(fn(x) = x * x, xs));

// Application code remains an ordinary call.
value total = sumOfSquares(samples);

The Language Design

A single semantic language, with cost and platform boundaries explicit where they matter.

Π

Proper Pi Types

Dependent application substitutes the accepted argument term. Universes are cumulative through checked lifting, not indiscriminate unification.

Σ

Proper Sigma Types

Dependent pairs, flat telescopes, existential abstraction, GADTs, and first-class evidence share one scoped representation.

Algebras and Morphisms

structure, algebra, and morphism elaborate to coherent evidence. Composition is explicit rather than guessed globally.

Laws Without Pretend Proofs

A law is a typed proposition with declared, tested, proven, trusted, or compiler-validated status. Declaration alone never creates Refl.

<T>

Typed Reflection and Derivation

Scope-safe semantic codes support computed data and algebra-owned derivation. Runtime type identity stays separate from provider layout.

First-Class OOP

Nominal classes, inheritance, dynamic dispatch, and checked subtyping are language concepts. Providers map them to native class models when contracts permit.

Deep Algebraic Effects

Row-polymorphic effects use deep handlers and scoped resumptions. Affine is the default; multi-shot control is requested explicitly.

~

Full Explicit Laziness

Strict evaluation is the default. Lazy(a), ~expr, and force provide memoized call-by-need with sharing and black-hole detection.

Repr and Transfer

repr controls semantic-to-storage conversion. Cross-target residence and transfer remain separate, typed operations.

Abstract Targets

Native code, managed runtimes, accelerators, processes, containers, and remote workers use one profile, capability, policy, and fallback model.

Canonical Surface Language

These examples follow the normative Language Reference. Some specified constructs remain implementation work.

Dependent Data and Existentials

Telescopes let later fields depend on earlier values. Existentials reuse Sigma introduction and scoped elimination rather than adding dynamic casts.

dependent.tl
type SizedVector(a:Type) =
    size:Nat * values:Vec(a, size);

type Showable = exists (a:Type).
    { value:a, display:a -> String };

value item : Showable =
    { value = 42, display = showInt };

unpack item as (a, x) in
    x.display(x.value)

Algebraic Contracts and Laws

Evidence is selected coherently and remains first-class after elaboration. Laws document algebraic intent, while pure intrinsic values let providers supply typed constants without changing the algebra.

algebra.tl
algebra Monoid(a:Type)
    extends Semigroup(a) = {
    value empty : a;

    law leftIdentity(x:a) =
        combine(empty, x) === x;

    law rightIdentity(x:a) =
        combine(x, empty) === x
};

instance Monoid(PackedPolynomial) = {
    function combine(x, y) = intrinsic;
    value empty = intrinsic
};

Deep Effects and Resumptions

Effects appear in function types. Handlers are deep, and resume is a scoped function binding whose multiplicity follows the effect contract.

choice.tl
effect Choice requires Resumption(Many) = {
    function choose[a:Type](options:List(a)) : a
};

handler FirstChoice : Choice = {
    function choose[a:Type](options:List(a)) =
        resume(head(options))
};

handle choose(values) with FirstChoice

Computed Data, Ordinary Evidence

Typed semantic codes can generate data without exposing target layout. Derivation then produces ordinary checked evidence with inferred field requirements.

codes.tl
type Source(a:Type) =
    None + Some * item:a;

function schema(code:DataCode(Unit, Z))
    : DataCode(Unit, Z) =
    renameDataType(code, "Generated");

type Generated(a:Type) =
    schema(dataCode(reflect(Source(a))));

instance Eq(Generated(a)) = derive;

Target-Aware Libraries, Ordinary Calls

Placement metadata belongs to declarations. Providers refine canonical functions inside target blocks; callers do not write execution blocks or processor names.

target.tl
function matrixMultiply(a:Matrix, b:Matrix)
    : Matrix on compute =
    portableMatrixMultiply(a, b);

target FastAccelerator {
    function matrixMultiply(a, b) = intrinsic;
};

value result = matrixMultiply(left, right);

First-Class Classes

Class declarations are canonical across targets. Subtyping elaborates explicit core coercions; target providers choose native or portable object layouts.

classes.tl
class Animal(name:String) = {
    function describe(self:Self) : String = self.name
};

class Dog(breed:String)
    extends Animal("unknown") = {
    override function describe(self:Self) : String =
        self.breed
};

value dog = Dog.new("Collie");

AWFY Performance

Tulam's balanced and throughput memory profiles compared with C++, Node.js, and GHC across all nine Are We Fast Yet workloads.

Benchmark Tulam balanced Tulam throughput C++ Node.js Haskell
Sieve21.0 µs25.0 µs21.9 µs51.0 µs75.6 µs
Queens22.0 µs22.0 µs17.1 µs95.9 µs9.421 µs
Bounce23.0 µs23.0 µs13.1 µs40.1 µs685.2 µs
Permute24.0 µs21.0 µs29.5 µs95.8 µs121.5 µs
Storage178.0 µs198.0 µs762.0 µs263.1 µs185.7 µs
Towers237.0 µs327.0 µs42.5 µs136.9 µs145.1 µs
List30.0 µs37.0 µs24.7 µs43.9 µs185.5 µs
Mandelbrot63.2 ms70.3 ms62.5 ms60.2 ms81.6 ms
NBody20.5 ms22.3 ms41.6 ms53.5 ms95.3 ms
Geometric mean vs C++1.04x1.14x1.00x1.90x2.70x

Local arm64 measurements from one five-backend run on 2026-08-09 using benchmarks/awfy/run_benchmarks.sh. Every backend uses one warm-up plus the median of five independent process runs, with compilation and startup excluded. Small workloads use 1,000 self-timed iterations, Storage uses 100, and NBody/Mandelbrot use 3. Tulam uses --fp-contract=off; C++ uses Apple clang 17 C++17 -O3, Node.js is 23.2.0, and Haskell uses GHC 9.10.3 -O2. Haskell repetitions are forced through runtime inputs; fractional microseconds are preserved. Lower is better.

Compiler Strategy

  • Strict evaluation by default
  • One canonical multi-target pipeline
  • Closed typed Stage-R and RuntimeCore
  • LLVM native provider
  • Verified ownership, bounded reclamation, and exact reuse

Mathematical Optimization

  • Validated law-gated rewrites
  • Shape specialization
  • SIMD and bulk fusion
  • Abstract accelerator providers

Benchmark Discipline

  • 120/120 backend parity checks
  • Strict AWFY failure detection
  • Positive timing and verification gates
  • Pinned, relocatable native toolchain

Current Status

The design is normative; implementation support is incremental. Snapshot: 2026-08-09.

Working Baseline

  • Strict type checking is the default
  • Standard library reaches zero strict type errors
  • Zero residual standard-library type metas
  • Typed project manifests, dependency graphs, and lockfiles
  • Compiled project-aware REPL over LLVM ORC

Verified Test Gates

  • 186-row catalog: 172 pass, 14 pinned future gaps
  • 50 phase programs with 266 native assertions
  • 31 project, product, dependency, and lock checks
  • Relocatable ORC and AOT release checks
  • Nine verified native AWFY workloads

Roadmap Order

  • Pi, Sigma, evidence, repr, and effect foundations
  • Multi-module project and lockfile foundation
  • Persistent compiled REPL and shipped toolchain
  • Compiled REPL and native semantic parity
  • Ownership, lifetimes, bounded reclamation, and reuse
  • Full explicit laziness
  • Targets, acceleration, interop, and concurrency

Get Started

Build with Haskell Stack, then check, build, or run a typed Tulam project or single source file through LLVM native.

terminal
# Clone and build
$ git clone https://github.com/aantich/tulam.git
$ cd tulam
$ stack build

# Open the persistent compiled shell (LLVM ORC, with isolated AOT startup fallback)
$ stack exec tulam

# In a directory containing tulam.project.tl
$ stack exec tulam -- check
$ stack exec tulam -- run
$ stack exec tulam -- test
$ stack exec tulam -- project graph

# Run compiler, project, and language checks
$ stack test
$ ./tests/run_conformance.sh
$ ./tests/run_projects.sh
$ ./tests/run_repl.sh

# Native build: preserve multiply/add rounding boundaries (default)
$ stack exec tulam -- build example.tl --backend native --entry main --fp-contract=off

# Optionally permit fused multiply-add contraction
$ stack exec tulam -- build example.tl --backend native --entry main --fp-contract=fast