(tulam)

The most expressive type system.
Every backend. Native performance.

Dependent types, algebraic effects, and a compiler targeting JS, .NET, and LLVM — faster than C++ on real benchmarks.

Get Started See Examples
hello.tl
// Sum types with pattern matching
type Maybe(a:Type) = Nothing + Just * val:a;

// Algebraic structures (typeclasses)
algebra Monoid(a:Type) extends Semigroup(a) = {
    value empty : a;
};

// Functions with dependent types
function map(f: a -> b, xs: List(a)) : List(b) = match xs
    | Nil         -> Nil
    | Cons(h, t)  -> Cons(f(h), map(f, t));

Why tulam?

A language designed from first principles, where the type system is the language.

λ

Two Primitives

The entire type system — sum types, records, classes, effects — derives from tuples and lambdas. No hidden machinery. Every abstraction compiles down cleanly.

Categorical Vocabulary

algebra equips one type. morphism relates two or more. extends builds hierarchies. Laws are compiler-verified propositions, not comments. The compiler derives compositions automatically.

Σ

Sigma Types

Full dependent pairs and existentials with flat runtime layout. O(1) field access, zero pointer chasing — maps directly to .NET objects, JS objects, and C structs.

Π

Pi Types & Universes

Dependent functions (x:A) → B(x), a cumulative universe hierarchy, and type-level computation that reuses the same evaluator as values. One language, not two.

Laws as Code

Algebras declare equational laws with ===. Not comments — the compiler carries them forward for rewrite-rule optimization and property test generation.

Composable Effects

Effects are row-polymorphic: no monad transformers, no lift. Swap handlers per call-site — mock IO for tests, change concurrency backends, sandbox modules.

Repr System

Bidirectional representation mappings between user types and machine types. expr as Type inserts conversions automatically — direction resolved from the repr map.

First-Class Classes

OOP classes with 1-1 codegen mapping. class compiles to an actual class on every target — .NET, JS, C++. Single inheritance, abstract, sealed, override, final.

Multi-Target Interop

Compile to JS, .NET, or native via LLVM. Algebra-based interop: write a pure contract, provide target-qualified instances. The compiler selects and inlines per build target.

By Example

tulam combines the elegance of ML with the rigor of type theory.

Algebras & Morphisms

An algebra equips one type with operations. A morphism relates two types directionally. Laws use === — they're not comments, they're compiler-verified propositions used for optimization and testing.

categorical.tl
algebra Monoid(a:Type) extends Semigroup(a) = {
    value empty : a;
    law left_identity(x:a) =
        combine(empty, x) === x;
};

// Morphisms relate two types
morphism Iso(a:Type, b:Type)
    extends Convertible(a, b) = {
    function unconvert(x:b) : a;
    law roundtrip(x:a) =
        unconvert(convert(x)) === x;
};

Sigma Types & Existentials

Full dependent pairs where later fields depend on earlier values. Existentials hide concrete types behind interfaces. Flat runtime layout — O(1) field access, no nested pairs.

sigma.tl
// Existential: hide the type, keep the interface
type Showable = exists (a:Type).
    val:a * show:a -> String;

// Pi types — return type depends on value
function replicate(n:Nat, x:a) : Vec(a, n) =
    match n
    | Z       -> VNil
    | Succ(k) -> VCons(x, replicate(k, x));

// Unpack existentials
unpack showable as (a, s) in s.show(s.val);

Composable Effect Handlers

Effects compose via row polymorphism — no monad transformer stacks, no lift. Swap handlers at the call site to mock IO, change backends, or sandbox modules. The same code runs pure or effectful.

effects.tl
effect Console = {
    function print(s:String) : Unit;
    function readLine() : String;
};

handler StdConsole : Console = default {
    function print(s) = putStrLn#(s);
    function readLine() = readLine#();
};

// Swap the handler — no code changes needed
greet [Console = TestConsole] ("world");

Repr System & Target Interop

Bidirectional representation mappings between user types and machine types. expr as Type auto-inserts conversions. Target-qualified instances let the compiler select platform-native implementations per build target.

interop.tl
// Map user types to machine types
repr Nat as Int default where {
    function toRepr(n:Nat) : Int = match n
        | Z       -> 0
        | Succ(m) -> 1 + toRepr(m);
};

// Target-qualified instances
target dotnet {
    instance Show(Int) =
        System.Int32.ToString;
};

First-Class OOP

Not an encoding — class compiles to a real class on every target. Single inheritance, abstract, sealed, override, final. Extend .NET/JS/C++ classes transparently from tulam source.

classes.tl
abstract class Shape(color:String) = {
    function area(self:Shape) : Float64;
    function describe(self:Shape) : String
        = "A " ++ self.color ++ " shape";
};

class Circle(radius:Float64)
    extends Shape = {
    override function area(self:Circle)
        : Float64 =
        3.14159 * self.radius * self.radius;
};

Performance

Are We Fast Yet benchmarks — tulam native vs C++, Haskell (GHC), and JavaScript (Node).

Benchmark C++ Haskell Node.js tulam native vs C++
List 8 µs 6 µs 50 µs 22 µs 2.7x
Queens 7 µs 3 µs 70 µs 43 µs 6.1x
Permute 11 µs 35 µs 82 µs 7 µs 0.6x
Sieve 6 µs 26 µs 96 µs 49 µs 8.1x
Towers 16 µs 48 µs 100 µs 105 µs 6.5x
NBody 8.8 ms 23.5 ms 11.8 ms 41.3 ms 4.6x
Bounce 5 µs 18 µs 145 µs 31 µs 6.2x
Storage 226 µs 85 µs 193 µs 206 µs 0.9x
Mandelbrot 19.5 ms 8.4 ms 18.6 ms 11.8 ms 0.6x
Geometric mean 1.0x 2.0x 8.6x 4.0x

Faster than C++  —  Lower is better. Ratios show time relative to C++. Benchmarks from Are We Fast Yet (objects, closures, arrays — no micro-tricks).

Current Status

tulam is under active development. Here's where things stand.

Language

  • Surface syntax finalized
  • Parser complete
  • Bidirectional type checker
  • Module system with caching
  • Standard library (13 modules)
  • 1050+ tests passing

Backends

  • Bytecode interpreter
  • LLVM native compilation
  • JavaScript codegen
  • .NET codegen

Coming Next

  • Full HKT support
  • Effect handler codegen
  • JS & .NET targets
  • Package manager

Get Started

tulam is built with Haskell Stack. Clone, build, and start the REPL in under a minute.

terminal
# Clone the repository
$ git clone https://github.com/aantich/tulam.git
$ cd tulam

# Build with Stack
$ stack build

# Start the REPL (loads standard library automatically)
$ stack exec tulam

tulam> 1 + 2
3

tulam> map(fn(x) = x * 2, Cons(1, Cons(2, Cons(3, Nil))))
Cons(2, Cons(4, Cons(6, Nil)))