Einlang

A source language for indexed tensor programs, recurrence, and local derivatives.

Keep the mathematical structure in the source.

Most tensor code knows the shape of every array and the role of no axis. Einlang keeps both on the page: named axes, explicit reductions, recurrent dependencies, and derivative requests that live beside the definitions they differentiate.

Indexed definitions Explicit reductions Local derivatives Declarative recurrence Python boundary

What to read

  • The Name in the Bracket: a book about what notation hides, and what happens when you refuse to let it.
  • Paper: compact language-design argument.
  • Thesis: compiler and runtime details.
60-second taste ein
let x = 1.0;
let y = x * x + 3.0 * x;
let dy_dx = @y / @x;
print(dy_dx);
01 Why

Tensor code should still show the important structure.

Too often, tensor programs disappear into helper calls, layer wrappers, and gradient machinery. Einlang keeps the axes, reductions, and derivatives on the page so the source still reads like the idea behind it.

You can see the axes

Indices and reductions are written directly instead of being hidden in strings or helper calls.

You can see the derivative

Gradient expressions live beside the original definitions, not in a separate autodiff API.

You can keep one notation

The same style carries from simple algebra to recurrences, numerics, and larger models.

The payoff

Programs get easier to inspect, easier to explain, and easier to trust.

02 Syntax

Syntax features, side by side.

This section is about syntax, not slogans. Each example shows one language feature in a more typical style and in Einlang, so you can see what the syntax keeps on the page.

Einstein notation

Indices and reductions are written directly instead of being hidden in a string or helper call.

NumPy / JAX

NumPy / JAX numpy
y = np.einsum("bi,ci->bc", x, W) + bias

Einlang

Einlang ein
let y[b, c] = sum[i](x[b, i] * W[c, i]) + bias[c];

Where clauses

Index relations stay attached to the expression instead of being managed as loop bookkeeping.

Python loops

Python loops python
for oh in range(H_out):
    for ow in range(W_out):
        out[oh, ow] = sum(
            inp[oh + kh, ow + kw] * kernel[kh, kw]
            for kh in range(KH)
            for kw in range(KW)
        )

Einlang

Einlang ein
let out[oh, ow] = sum[kh, kw](
  input[ih, iw] * kernel[kh, kw]
) where ih = oh + kh, iw = ow + kw;

Autodiff syntax

The derivative is part of the program text, not a separate API wrapped around it.

JAX / framework style

JAX / framework style jax
def loss_fn(W):
    logits = x @ W.T + bias
    probs = jax.nn.softmax(logits)
    return -(target * jnp.log(probs)).sum()

dloss_dW = jax.grad(loss_fn)(W)

Einlang

Einlang ein
let logits[b, c] = sum[i](x[b, i] * W[c, i]) + bias[c];
let probs[b, c] = exp(logits[b, c]) / sum[k](exp(logits[b, k]));
let loss = -sum[b, c](target[b, c] * log(probs[b, c]));
let dloss_dW = @loss / @W;

Fibonacci recurrence

The recurrence uses the same tested form as the repo examples: base cases first, then the recursive rule.

Python loop

Python loop python
fib = np.zeros(11, dtype=int)
fib[0] = 1
fib[1] = 1
for n in range(2, 11):
    fib[n] = fib[n - 1] + fib[n - 2]

Einlang

Einlang ein
let fib[0] = 1;
let fib[1] = 1;
let fib[n in 2..11] = fib[n-1] + fib[n-2];

Comprehensions and ranges

Generated arrays stay as expressions, and ranges are part of the syntax.

Python

Python python
squares = [i * i for i in range(1, 10)]

Einlang

Einlang ein
let squares = [i * i | i in 1..10];

Block expressions

Multiple statements can still produce a value, so setup code stays inside the expression that uses it.

Python

Python python
tmp = x + y
if tmp > 0:
    result = tmp * scale
else:
    result = 0

Einlang

Einlang ein
let result = {
  let tmp = x + y;
  if tmp > 0 { tmp * scale } else { 0 }
};
03 Read

Choose the artifact that matches your question.

Each document answers a different question: how to learn the notation, what the language-design argument is, how the implementation works, or how to run the code.

Book

Notation determines what you can notice. Sixteen chapters tracing one idea — the coordinate audit — from a Tuesday bug through gradients, recurrence, attention, and dynamic routing, to the principle that organizes them all.

Paper

The compact research argument for keeping indexed definitions, recurrence, and derivative requests in one source language.

Thesis

The fuller implementation writeup, including frontend identity, IR passes, recurrence analysis, autodiff lowering, runtimes, and tests.

Repository

Use the repository for installation, commands, examples, and contribution pointers.

04 Evidence

What makes the claim concrete.

The project is still experimental, but its claims are attached to an implementation: parser and IR passes, executable examples, tests, and generated research artifacts.

Source facts survive lowering

The compiler keeps axes, ranges, recurrence dependencies, and derivative requests available until the pass that consumes each fact.

Examples cover the surface

The repository includes small checks, recurrence suites, optimization examples, and model-shaped programs that stress the same notation.

05 Next

Choose your next step.

From here, choose the document that matches your intent. Runnable commands live with the repository docs, where they can stay close to the code.