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.
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.
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 feeWhy 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.