Live demo · Resolver interpreter

Write Python.
Run native code.

Chalk reads your Python resolvers without running them and compiles every one it can into a vectorized native expression. Type a function below and watch which lines compile, what they become, and exactly which line keeps a resolver in Python.

Type a resolver

Pick an example or write your own typed def. It re-analyzes as you type. Green lines compile natively; a coral line is where the interpreter stops and the resolver falls back to Python.

resolver.pyBranches become a native conditional.
1
2
3
4
5
6
rust symbolic interpreterwaiting

Analyzing the function…

The analysis runs live on a Chalk scaling group serving the Rust port of Chalk’s static accelerator. Your code is parsed and executed symbolically, never run. Parameter types come from the annotations: int, float, str, bool, bytes, list[...] and Optional[...].

How it works

A resolver is a function of features. If Chalk can prove what it computes for every possible input, it can compute that for a whole column at once.

Parse

The resolver's source and its parameter types are all the interpreter needs. It parses the function; nothing in it ever runs.

Execute symbolically

Each statement is evaluated over symbolic values instead of data: branches become conditionals, loops become list functions, and Python's own error cases become guards.

Lower to an expression

The result is a typed Chalk expression that runs vectorized over a whole column. If any line has no native equivalent, the resolver keeps running as Python, and you get the line and the reason.

Getting started

Nothing to opt into

Write resolvers the way you already do. Chalk tries to compile every Python resolver when it plans a query, and the ones that compile run natively without a code change.

In ChalkSQL, a Python function body goes through the same compiler, so a helper you write once in Python runs as a native SQL function. The analyze endpoint behind this page is there too, if you want to check a function from a script.

from chalk import online
from chalk.features import features


@features
class Transaction:
    id: int
    amount: float
    is_international: bool
    fee: float


# Plain Python. When Chalk plans a query, it traces the body and, if
# every line has a native equivalent, runs it as a vectorized expression.
@online
def processing_fee(
    amount: Transaction.amount,
    is_international: Transaction.is_international,
) -> Transaction.fee:
    fee = amount * 0.029 + 0.30
    if is_international:
        fee = fee + amount * 0.01
    return fee

Why compile resolvers

Python is the right language to write features in and the wrong one to run them in, row by row, at serving time.

No Python on the hot path

A compiled resolver runs inside Chalk's native engine, a column at a time, instead of calling a Python function once per row.

Python semantics, kept

Division by zero, math domain errors and None handling compile to the same failures Python would raise, so a compiled resolver can't quietly disagree with its source.

Exact fallbacks

When a line can't be lowered, the interpreter points at it and says why. Fix that line, or leave it: the resolver still runs, in Python.

Written in Rust

This demo runs the Rust port of Chalk's static accelerator. It needs no Python interpreter, and it checks a typical resolver in about a millisecond.

Modules in scope

math, re, json, datetime, hashlib and statistics resolve without an import, along with the string, list and dict methods resolvers lean on.

Same compiler in SQL

CREATE FUNCTION ... LANGUAGE PYTHON uses this compiler too, so a Python helper becomes a native SQL function rather than a UDF.

Keep writing Python

Let Chalk turn it into native code where it can. Talk to an engineer about your feature pipelines.