Python’s syntax.
Machine code.
Every core.

pyalt is a small compiled language with Python-like syntax. It builds standalone native executables — no interpreter, no GIL. parallel for spreads a loop across every core, and data races are compile errors.

  • 216 tests
  • CI: Windows + Linux
  • ~200 KB executables
  • zero runtime deps
  • MIT
mc_backtest — 10,000,000 loop steps

>

Python 3.11
pyalt
pyalt parallel

trades=84770  wins=21929  mean_eq=8165
✓ output byte-identical across all three

replay at 1:1 wall-clock time — you are actually waiting for Python

parallelism

The parallel version changes one word

200 simulated price paths, 50,000 steps each, an EMA-crossover strategy with stop-losses — per-step state that cannot be vectorized. The serial and parallel programs differ by a single keyword.

mc_backtest.pya — 0.049 s
eqs = [0.0] * paths
for p in range(paths):    r = run_path(p + 1, steps)
    eqs[p] = r.equity
mc_backtest_par.pya — 0.0089 s
eqs = [0.0] * paths
parallel for p in range(paths):    r = run_path(p + 1, steps)
    eqs[p] = r.equity
Python 3.11
pyalt
pyalt parallel

the same three times drawn to linear scale — pyalt’s are the slivers

safety

Your data race doesn’t compile

Every threaded language has the same horror story: works in testing, corrupts data in production. pyalt’s answer: assigning to shared state inside parallel for is rejected before the program ever runs. This is the compiler’s actual output:

pyalt check race.pya
race.pya:6:5: error: cannot assign to outer variable 'total'
inside parallel for — iterations race; write results into an
output list instead
        total = total + score(i)
        ^

The one blessed pattern — writing results into distinct slots of an output list — is race-free by construction. And since pyalt functions cannot touch globals, every function called from a parallel loop is thread-safe automatically. Appending to a shared list, writing a shared dict, returning from inside the loop: all compile errors, each with an explanation.

benchmarks

Measured, verified — losses included

Monte Carlo backtest, 10 million loop iterations, identical logic in each implementation, byte-identical outputs, median of 5 runs on Windows 11 x64. The harness re-verifies pyalt’s output against CPython’s on every run.

implementationserialparallelnotes
CPython 3.113.088 s GIL prevents thread parallelism
Numba 0.59 @njit0.041 s0.0068 s ~2.2 s JIT compile on first call; prange races are silent
pyalt0.049 s0.0089 s ahead-of-time; no warmup; races are compile errors
A benchmark table without the losses is marketing, not measurement. Numba wins this workload by 15–25% at steady state. pyalt differs in kind: it compiles whole programs rather than decorated functions, produces executables that run without Python installed, and rejects shared-state races at compile time instead of silently corrupting results.

Serial string- and dict-heavy code is the weaker case — roughly 2.5–5x over CPython, whose string and dict internals are already optimized C; parallel for reached 15x on the same text workload. Full methodology in BENCHMARKS.md; reproduce everything with python bench/harness.py.

language

A real language, not a demo

Every feature below is covered by the 216-test suite that CI runs on Windows and Linux. Annotate function parameters; everything else is inferred at compile time.

classes & inference
class Result:
    equity: float
    trades: int
    wins: int

def run_path(seed: int, steps: int) -> Result:
    price = 100.0       # float, inferred
    ...
    return Result(equity, trades, wins)
exceptions
try:
    values = parse_row(line)
    total = total + values[2]   # bounds-checked
except as msg:
    print(f"skipping row: {msg}")

raise "empty input file"
modules
from utils import clean
import stats

rows = clean(read_lines("data.csv"))
m = stats.mean(rows)
# the import graph compiles into ONE binary;
# import cycles are compile errors
call it from Python
> pyalt buildpy metrics.pya
build\metrics.pyd

# then, in ordinary Python:
import metrics
metrics.sharpe(returns)   # native speed

read before porting

Python’s syntax — not always Python’s semantics

Machine performance costs some Python behaviors. These are the differences that matter, stated up front:

install

Running in three steps (Windows x64)

  1. 1

    Download and unzip

    Get pyalt-0.1.0-windows-x64.zip (7 MB, self-contained — no Python required) and unzip anywhere, e.g. C:\pyalt.

  2. 2

    Add it to your PATH

    The included script appends bin\ to your user PATH. Windows requires downloaded scripts to be run explicitly — then open a new terminal:

    PowerShell
    powershell -ExecutionPolicy Bypass -File .\install.ps1
  3. 3

    Compile something

    Building needs a C compiler on the machine — MSVC (free Build Tools), gcc, or clang, detected automatically. Save as hello.pya:

    hello.pya
    def fib(n: int) -> int:
        if n < 2: return n
        return fib(n - 1) + fib(n - 2)
    
    print(f"fib(30) = {fib(30)}")
    run it
    pyalt run hello.pya      # compile and run
    pyalt build hello.pya    # build\hello.exe — ~200 KB, zero deps
    pyalt buildpy hello.pya  # build\hello.pyd — import from Python

Linux and macOS run from source — python3 pyalt.py run prog.pya (Python 3.10+); the full test suite passes on Ubuntu in CI, less field-tested than Windows.

SHA256 pyalt-0.1.0-windows-x64.zip
A889628D820D0D84E8584FE792F56524C6F3A8EE13A8DA62D943B95560BDC60B

how it works

Source to machine code, no interpreter in between

The runtime is a single C header (~1,300 lines): a conservative mark-sweep garbage collector, insertion-ordered hash tables with cached string hashes, zero-copy string views, a thread pool for parallel for, and bounds checks throughout. The compiler (~3,700 lines) is currently bootstrap-hosted in Python — the usual arrangement for a young language; the release package is self-contained, and compiled output never involves Python.

Compile errors carry position, context, and a suggestion:

pyalt check prog.pya
prog.pya:3:19: error: '+' has mismatched operand types: int and str
— to build a string, convert with str(...) or use an f-string
        total = total + line
                      ^

roadmap

What comes next, in order

Development practice: no change merges without tests, and the benchmark harness verifies pyalt’s outputs against CPython’s on every run.