Skip to content

Introduction — What Zolo is, and why it exists

Zolo — modern Lua, 2026.

Zolo is the typed, ergonomic language of the Lua ecosystem. It compiles to pure Lua 5.1 bytecode (no extra runtime) and also has a native path (Cranelift) and WASM. It is the TypeScript moment for Lua.

This page exists to explain why the language exists before teaching syntax. If you want to jump straight into code, head to Getting Started.


The gap in the Lua ecosystem #

Lua is one of the most widely used real languages in the world — and almost nobody talks about it, because it lives hidden inside other things:

  • Roblox / Luau — ~100M active users, millions of scripters.
  • Neovim — the entire configuration and plugin system.
  • Redis — atomic scripts via EVAL.
  • Garry's Mod, World of Warships, Don't Starve, Factorio, Stormworks — gameplay and modding.
  • OpenResty / Kong / HAProxy — edge computing, gateways, filters.
  • Wireshark — dissectors.
  • Embedded — anything that needs scripting with a 200KB runtime.

In all of these places, the alternative is plain Lua. And plain Lua is painful the moment you want to write something serious:

  • No types — the bug shows up in production, not in the editor.
  • No match, no traits, no enums with payloads.
  • Confusing scope: local everywhere, or forget it and create a global.
  • 1-indexed (everyone has tripped on this).
  • Poor tooling — generic autocomplete, limited debugger.
  • require is a glorified dofile, with no real typed module system.

Attempts to improve this exist:

  • Teal and Luau add types to Lua, but conservatively — Lua plus annotations.
  • MoonScript is dead.
  • Fennel is a Lisp — great, but a niche audience.
  • Haxe compiles to Lua, but it's a general-purpose language, not designed for the Lua ecosystem.

Nobody has claimed the spot of "the TypeScript of Lua" — a modern language with pattern matching, traits, generics, decorators, and first-class tooling, that compiles to Lua bytecode and runs drop-in in any Lua host.

That's the gap Zolo fills.


The 6 pillars #

1. Typed by default, gradual when useful #

Types are the main thing. But you don't have to annotate everything all the time.

let mut x = 0          // infers int
let mut name = "Alice" // infers str
let users: [User] = []  // annotate when you need to

fn greet(u: User) -> str {
    return "Hello, {u.name}"
}

Want to escape the type system? There's any — but it warns unless you opt in with #[allow_any]. The language doesn't stop you from being dynamic; it just tells you when you are.

2. Compiles to pure Lua 5.1 bytecode, no runtime #

This is the killer feature. Zolo emits Lua 5.1 that runs in any compatible Lua host — no patch, no hook, no extra runtime.

bash
zolo compile script.zolo > script.lua
# script.lua runs in Roblox, Neovim, Redis, GMod, OpenResty, Wireshark...

You write a Neovim plugin in Zolo. You write a Roblox script in Zolo. You write an OpenResty filter in Zolo. Same language, same tools.

// The same code that will run inside Neovim as a plugin
struct Buffer {
    id: int,
    name: str,
}

fn current_buffer() -> Buffer? {
    let id = vim.api.nvim_get_current_buf()
    if id == 0 { return nil }
    return Buffer { id, name: vim.api.nvim_buf_get_name(id) }
}

3. Native path (Cranelift) and WASM, with parity #

When there's no Lua host — you want a CLI, a daemon, a standalone binary — Zolo also compiles to:

  • Native via Cranelift (Linux/macOS/Windows binaries).
  • WASM (browser, edge runtimes, WASI plugins), with both a runtime path and an emerging ahead-of-time (AOT) path.

The semantics are the same. You pick the target at compile time.

bash
zolo build --target lua      # Lua 5.1 bytecode (default)
zolo build --target native   # native binary
zolo build --target wasm     # WebAssembly

Honesty: the embedded Lua 5.1 VM (NaN-boxing, tri-color GC, 38 opcodes) is about 5x slower than LuaJIT. Performance is on the roadmap, not the focus today. The current focus is ergonomics and portability. If you need C-speed hot loops, use the native backend or run the emitted Lua under LuaJIT.

4. First-class tooling from day 1 #

There's no "we'll add an LSP later." The Language Server already covers go-to-definition, references, rename, inlay hints, semantic tokens, code actions, diagnostics, call hierarchy, and more. All of this exists from the start:

  • zolo-lsp — full Language Server.
  • zolo-fmt — formatter.
  • DAP — debug adapter protocol: breakpoints, step, watch.
  • zolo repl — REPL.
  • VS Code extension in editors/vscode/.
  • Hot-reload in development.

All of this before 1.0 — because an editor with no tooling is the first pain anyone coming from TypeScript or Rust feels.

5. Idiomatic plugins, not raw FFI #

Zolo plugins are Rust cdylibs, but the interface you use doesn't leak Rust. Consumers never notice.

use http

let res = http.get("https://api.example.com/users")
let users = res.json() as [User]

for u in users {
    print(u.name)
}

No ffi.cdef, no manual casts, no leaking binding details. Plugins have types, autocomplete, and hover.

There are 180+ plugins implemented as Rust cdylibs today (crypto, json, http, datetime, image, http-server, and many more). See PLUGIN_ROADMAP.md.

6. Honesty about costs #

Zolo does not promise "zero cost." Every allocation that happens is, as much as possible, surfaced through lints:

  • "This operation allocates a new table."
  • "This match on a string is O(n) — consider an enum."
  • "You're using any here — you lose static checking."

You decide whether to accept the cost. But you know. There's no hidden magic.


Where Zolo shines #

  • Neovim plugins with real types, without becoming a giant.
  • Roblox/Luau scripts that are modern, with pattern matching and traits.
  • Edge functions on OpenResty/Kong with decent syntax.
  • Game modding for titles that use Lua (GMod, World of Warships, indie games).
  • Complex Redis scripts with static checking.
  • Creative coding and game scripting (a compatible subniche — vibe-coder, wgpu/winit/rapier plugins).
  • Standalone CLIs / daemons via the native backend, when there's no host.

Where Zolo is not the best choice

Honesty first:

  • Systems programming (kernels, drivers, runtimes). Use Rust or Zig.
  • Numeric hot paths without a LuaJIT or native target. The default Lua 5.1 VM is slow.
  • Deterministic GC (hard real-time, sample-level audio). Use Rust.
  • Native mobile apps with their own SDK (Swift/Kotlin). Zolo has no UIKit/Android bindings.
  • A huge existing library ecosystem like Python or Node has today. Zolo is pre-1.0.

Zolo is not a replacement for Rust, Swift, or TypeScript in general. It's an upgrade for Lua — and it's good exactly where Lua is already the right choice.


End-to-end mini-example #

use std::Array

// users.zolo
struct User {
    name: str,
    age: int,
    email: str?,  // optional
}

trait Greetable {
    fn greet(self) -> str
}

impl Greetable for User {
    fn greet(self) -> str {
        return match self.age {
            a if a < 18 => "Hi kid {self.name}",
            _ => "Hi {self.name}",
        }
    }
}

fn fetch_users() -> [User] {
    return [
        User { name: "Alice", age: 30, email: "alice@x.com" },
        User { name: "Bob", age: 12, email: nil },
    ]
}

fn main() {
    let users = fetch_users()

    users
        |> Array.filter(|u| u.age >= 18)
        |> Array.map(|u| u.greet())
        |> Array.each(print)

    // First-class temporal literals
    every 5s {
        print("heartbeat from {users.len()} users")
    }
}

main()

Compiles to:

  • Lua 5.1 bytecode → runs in Neovim, Roblox (with task.wait mapping), GMod, OpenResty.
  • Native via Cranelift → standalone CLI binary.
  • WASM → browser or edge.

Same source. Three targets. No #ifdef.


Status (important) #

Pre-1.0 (0.1.0). It compiles, runs, and tests. APIs still change.

  • Compiler: 25 Rust crates.
  • Roadmap: ~68% complete — see PROGRESS.md.
  • Plugins: 180+ implemented as Rust cdylibs (PLUGIN_ROADMAP.md).
  • Tooling: LSP, formatter, DAP, REPL — all functional.

Use it to write scripts, plugins, experiment, and give feedback. Don't use it in critical production yet.


Next #

enespt-br