Getting Started
🐱 Write Python-shaped code; ship a native module. 🐆
cheatah is a compiled, Python-shaped language. You write a .purr file, compile it with purrc into a native shared module (.so), and the cheatah runtime loads and runs that module. This page walks the whole path: hello-world, then what happens when you compile — and what "statically linked" and "dynamically loaded" mean here.
Hello, cheatah
import io
io.print("meow")Save it as hello.purr, then:
purrc hello.purr -o hello.so # compile to a native module
cheatah hello.so # the runtime loads & runs itmeowpurrc is the compiler; cheatah is the runtime host that loads a compiled module. (Build both from source with the release preset: cmake --preset release && cmake --build --preset release.)
What
purrc is a transpiler in front of your system C++ compiler. It lexes and parses the .purr, generates modern C++, and invokes the C++ backend directly (fork + execvp, never a shell — no command injection):
hello.purr
│
│ lex → parse → AST
▼
┌─────────────────────┐
│ purrc (transpiler) │ generates modern C++ (one .cpp)
└─────────────────────┘
│
│ emits hello.so.gen.cpp (kept next to the output, so you can read it)
▼
┌─────────────────────────────────────────────────────────────┐
│ C++ backend: c++ -std=c++20 -O3 -march=native -fPIC -shared │
│ + libcheatah_<module>.a (imported modules) │
└─────────────────────────────────────────────────────────────┘
│
▼
hello.so ── a native shared object exporting: extern "C" void purr_main()Two things worth noticing:
It compiles at
-O3 -march=native. The module is optimized native code for your CPU — that's the whole performance bargain (see Performance).It links only what you
import. Each imported stdlib module (io,ndarray,linalg, …) contributes one static archive; import nothing, pull in nothing.
Static
cheatah's module model uses both linking styles, each where it pays off:
┌───────────────────────── hello.so ─────────────────────────┐
STATIC LINK │ your compiled code + libcheatah_io.a │
(at compile) │ libcheatah_ndarray.a (etc.) │
│ the stdlib you imported is baked IN — the module is │
│ self-contained, no separate cheatah libraries at runtime │
└─────────────────────────────────────────────────────────────┘
▲
│ dlopen("hello.so") + dlsym("purr_main")
│
DYNAMIC LOAD ┌──────────────────────────────────────────────────────────┐
(at run time) │ cheatah (the runtime host) │
│ validates the file, loads the module, calls purr_main() │
└──────────────────────────────────────────────────────────┘Statically linked stdlib (compile time). The modules you imported are compiled into the
.soas static archives (libcheatah_*.a). The module is self-contained: no separate "cheatah runtime library" to ship or find on the load path.Dynamically loaded module (run time). The
.sois not a standalone executable — it exportsextern "C" void purr_main(). Thecheatahhostdlopens the module, resolvespurr_main, and calls it. Before loading, the runtime validates the file (canonical path, regular file, refuses world-writable, checks the ELF magic — see Security).
That dlopen step is the dynamic-loading mechanism interpreted languages use for plugins and hot-reload — except here the loaded code is compiled native, so it runs at full speed. (Performance covers how that gives you interpreter-style dynamism without an interpreter.)
Where to go next
Coming from Python — bring an existing Python script over with light edits.
Performance — why the compile-time cost buys run-time speed, and how the
@perfnumbers are measured.Security — what the language protects you from, and what you still own today.
Browse the Modules in the sidebar (
io,ndarray,linalg,string, …) for the full standard-library reference.
