Welcome to tulam
tulam is an experimental high-level programming language with an unusually wide ambition: make very expressive types, practical application programming, native performance, and several host platforms belong to one coherent language.
This guide begins with ordinary programs and introduces the deeper ideas only when they become useful. You do not need to know type theory, category theory, Haskell, or compiler construction to begin.
Why another language?
Programming languages often make us choose early:
- a productive high-level language or a language designed for low-level speed;
- an expressive type system or straightforward interoperability;
- native executables or a managed and browser ecosystem;
- mathematical abstraction or familiar application-oriented tools.
tulam asks whether these choices can be postponed—or sometimes removed.
The goal is a language that is:
- high level, with algebraic data, pattern matching, functions, objects, effects, and concise reusable abstractions;
- type expressive, with proper dependent functions and products, cumulative universes, existentials, GADTs, and checked evidence—closer to the territory of the Calculus of Constructions than Haskell’s ordinary surface type system;
- multi-target, with one checked program model that can be implemented by native, .NET, JavaScript, and eventually other providers;
- at home on each platform, with typed foreign interoperability rather than a lowest-common-denominator runtime sealed away from the host ecosystem; and
- performance-oriented, with specialization, unboxing and representation choices, target-aware implementation overlays, and explicit numerical build policies such as fast floating-point contraction.
In deliberately over-ambitious shorthand, the dream is “one language to rule them all.” No existing language has completed this exact combination. That is not evidence that the attempt is pointless; it is evidence that the design has to be explicit, testable, and honest about what works today.
One language, not several dialects
Multi-target compilation can easily become several vaguely related languages sharing a parser. tulam takes a stricter approach:
Tulam source
↓
strict checking and typed elaboration
↓
one closed, verified, optimized RuntimeCore program
↓
target provider
├── LLVM native available today
├── .NET planned
├── JavaScript planned
└── further targets future providers
A provider may choose an efficient representation, map classes to a host object model, call platform APIs, or use target-specific instructions. It may not reinterpret the source language or quietly skip its static guarantees. This is what makes “the same language on different targets” a technical contract rather than a slogan.
Interoperability follows the same principle. Foreign nullability, exceptions, mutation, asynchronous behavior, and ownership must be represented by checked types, effects, or boundary declarations. Platform convenience should not become invisible unsafety.
Expressive types, explained gently
Most languages let a function’s result value depend on an argument value. tulam also lets its result type depend on an argument:
function replicate[a:Type](count:Nat, item:a) : Vec(a, count) = ...;The result records its length. A caller that supplies count receives a vector whose type contains that same value. This is a dependent function, traditionally called a Pi type.
Data can be dependent too:
type SizedVector(a:Type) =
size:Nat * values:Vec(a, size);The type of values refers to the preceding size field. This is a dependent product, traditionally called a Sigma type. You will learn both from concrete examples before the guide asks you to reason about their formal names.
The aim is not to turn every program into a proof. Ordinary code should remain ordinary. Richer types are tools for the places where a useful fact—dimensions, protocol state, available effects, a representation, a target capability—can prevent an entire class of mistakes.
The small core: lambdas and telescopes
Two ideas organize tulam’s core.
A lambda is a function: it binds inputs and produces a result. Named functions, anonymous functions, methods, and many higher-level constructs eventually share this machinery.
A telescope is an ordered n-tuple of fields in which each field may depend on earlier fields. Ordinary tuples, records, constructor payloads, function parameters, argument lists, dependent products, evidence packages, and class fields all use this one idea.
For example:
{3, True, "three"}is one flat three-field tuple. It is not compiler-generated shorthand for a pair containing another pair. Explicit nesting remains explicit:
{3, {True, "three"}}This distinction persists through checking and compilation. It gives the language a uniform semantic model while leaving target providers free to unbox or otherwise optimize a representation without changing its meaning.
Performance without a second language
tulam’s performance goal is not to bolt a separate “kernel language” onto an otherwise high-level system. The compiler first produces one checked executable core. Optimization and target lowering happen behind that boundary.
For numerical work this permits, over time:
- specialization of polymorphic and evidence-using code;
- compact primitive and user-defined representations;
- flat tuple and constructor layouts;
- SIMD and bulk-computation providers;
- target-specific intrinsic implementations; and
- explicit build policies where numerical semantics admit a faster choice.
One example already exposed by the native toolchain is floating-point contraction. The default --fp-contract=off preserves separate multiply and add operations. --fp-contract=fast permits contraction into a fused operation where supported. This is an artifact-wide, visible choice—not an undocumented change made by a backend.
“Fast” must always mean “fast within the selected semantic contract.”
What works today
LLVM native is the sole reference implementation and the current execution backend. It is the target used for routine correctness, conformance, and performance testing.
The current implementation has substantial support for ordinary functional programming, algebraic data, Pi and Sigma types, structures and evidence, classes, effects, representations, projects, and native compilation. Some specified areas remain incomplete, notably:
- .NET, JavaScript, and other target providers;
- the general target-planning and fallback graph;
- explicit call-by-need laziness end to end;
- structured concurrency, channels, actors, and STM;
- recursive lexical handler support in the native backend; and
- separate compiled library artifacts.
The exact list changes as development proceeds. This guide marks incomplete features where they appear, while the Implementation Plan remains the authoritative status record. The Language Reference defines the language, including specified features that may still have an implementation gap.
How to use this guide
The first five chapters form a quick start:
- this chapter explains the purpose and shape of the language;
- Installing tulam prepares the compiler and editor;
- Your first program checks and runs one source file;
- Check, run, build, and test introduces the normal project workflow; and
- A guided tour combines several language features in one small program.
After that, the guide proceeds from everyday expressions and data toward the type system, abstraction, effects, objects, targets, and larger projects. The complete table of contents offers alternative entry points.
Examples use the canonical surface language. A status callout means “this is part of the specified language, but current execution support has a named limit.” It never means “use this different syntax for now.”
A useful mindset
You can begin by treating tulam as a strict functional language with familiar data declarations, functions, and pattern matching. The deeper machinery will still be there when a problem needs it.
The most productive question is not “How much type theory must I learn first?” It is “What fact would make this program easier to use correctly?” tulam’s type system gives you room to answer that question more precisely as your program grows.