The J2 Programming Language

J2 is a small language with an unusual pair of properties: programs start instantly through an interpreter, and the same programs compile to native code that uses every core of your machine, without a single annotation.

You write plain code: constants and mutables, functions, sequences and maps, classes, typed error handling. Running j2 on a file executes it immediately, with no build step. When a program is compute heavy, j2 build produces a native binary and recognizes the loops and independent calls that can safely run in parallel. The result is scripting-language ergonomics with compiled-language speed where it matters.

A first look

# Find the longest word.
words = ["apple", "banana", "fig", "grapefruit"]

func longest(ws) = {
    best := ""
    for w in ws {
        if len(w) > len(best) { best = w }
    }
    give best
}

print(fmt("longest: {} ({} chars)", longest(words), len(longest(words))))

Five things J2 considers important are already visible here. Bindings are constant by default (=) and mutable only when you say so (:=). Functions return with give. Loops read like sentences. Text is formatted with fmt and {} placeholders. And nothing here needed a type annotation, although the language accepts them and rewards them with native speed.

What makes J2 different

Two engines, one behavior

J2 ships an interpreter and a native compiler in a single command. The interpreter gives you the immediacy of a scripting language; the compiler gives you machine code. Both are held to the same standard: a program must produce identical output on either engine, and the test suite runs every program on both to keep that promise honest.

Parallelism you do not write

Native builds recognize common shapes of compute: reductions over large sequences, element-wise loops, matrix times vector kernels, and functions that make several independent calls. When a shape is provably safe to split, the compiled program runs it across your cores. There are no threads, locks, or task APIs in the language at all. The Automatic Parallelism chapter explains exactly what fires and when.

Deny-by-default capabilities

A J2 program cannot touch the filesystem, spawn processes, or reach the network unless you grant that capability on the command line. A script you downloaded cannot quietly read your files; j2 report.j2 refuses, and j2 --allow-fs report.j2 grants exactly one power. See Command Line.

Self-contained toolchain

Installing J2 installs everything, including the native compiler and the libraries it links against. There is no package manager to configure and no network dependency at build time. A laptop with no development tools can compile J2 programs offline.

How these docs are organized

Getting Started installs the toolchain and walks through your first programs. The chapters under The Language cover J2 from the ground up and are written to be read in order, though each stands alone; together they document every construct in the language. The Reference section covers the standard library function by function, the command line, the parallelizer, and how the two execution engines work.

Examples in these pages are complete programs unless presented as fragments, and every complete example was run against J2 0.1.0 while building this site.