Roberto Ierusalimschy Builds Lua for C Host Embedding
Ryan Peterman
Lua achieves extreme runtime speed and memory isolation because it is intentionally engineered as an embeddable library rather than an independent standalone engine.
Lua distinguishes itself from conventional scripting languages by being architected from inception as an embeddable C library rather than an extending runtime, ensuring host applications retain full control of the main execution loop. (0:43)
Unlike ahead-of-time or standard JIT compilers, trace compilers like LuaJIT record hot loops across inlined function calls, but they suffer extreme deoptimization penalties when reconstructing execution stacks on guard failures. (14:20)
Dynamic language evaluation via eval creates a structural barrier to ahead-of-time compilation, as bundling a dynamic compiler inside runtime binaries introduces significant overhead and complexity. (23:21)
Lua's 1-based indexing was a deliberate design choice prioritizing non-programmer intuition, contrasting with 0-based indexing which originated as an implementation detail of C pointer arithmetic offsets. (45:10)
Detailed notes
## Background
This interview was published on August 3, 2026, by Ryan Peterman on YouTube. In the video, Peterman interviews Roberto Ierusalimschy, the creator of the Lua programming language, discussing Lua's architectural strengths, foundational principles of programming language design, and predictions regarding the impact of AI on software development.
## TL;DR
Roberto Ierusalimschy breaks down the foundational design philosophy that separates Lua from other scripting languages, emphasizing its core identity as a lightweight, embeddable C library rather than an expansive standalone environment. He explores technical trade-offs across virtual machine interpretation, trace compilation in LuaJIT, dynamic code evaluation, and explicit sandboxing mechanisms. In addition, Roberto reflects on avoiding language bloat caused by design committees, the historical rationale behind 1-based indexing, and why AI code generation will demand simpler, more inspectable programming languages rather than complex ones.
## Atmosphere & style
The conversation carries an intimate, cerebral, and unhurried academic tone, blending deep technical systems analysis with witty, candid observations about software culture. Roberto speaks with the calm, self-effacing confidence of a veteran computer scientist who has spent decades refining a minimalist craft, frequently chuckling at industry dogmas, committee bloat, and modern quick-fix tutorials. The dynamic feels like a thoughtful masterclass between an inquisitive systems engineer and a foundational language designer, grounded by an enduring appreciation for simplicity, intellectual honesty, and foundational computer science.
## Bulletin
- [00:43](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=43s) Lua distinguishes itself from conventional scripting languages by being architected from inception as an embeddable C library rather than an extending runtime, ensuring host applications retain full control of the main execution loop.
- [14:20](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=860s) Unlike ahead-of-time or standard JIT compilers, trace compilers like LuaJIT record hot loops across inlined function calls, but they suffer extreme deoptimization penalties when reconstructing execution stacks on guard failures.
- [23:21](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1401s) Dynamic language evaluation via eval creates a structural barrier to ahead-of-time compilation, as bundling a dynamic compiler inside runtime binaries introduces significant overhead and complexity.
- [45:10](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2710s) Lua's 1-based indexing was a deliberate design choice prioritizing non-programmer intuition, contrasting with 0-based indexing which originated as an implementation detail of C pointer arithmetic offsets.
## Key takeaways
- [00:43](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=43s) Scripting languages and dynamic languages are frequently conflated, but true scripting implies a dual-language architecture focused on coordinating external systems and native code.
- [04:05](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=245s) Designing a language as a library allows deterministic memory reclamation, multi-state isolation without global state, and robust cross-language exception handling.
- [08:35](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=515s) Lua's performance advantage over dynamic peers like Python stems from avoiding features that cannot be implemented efficiently and keeping its virtual machine small enough to fit within CPU instruction caches.
- [14:20](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=860s) LuaJIT achieves exceptional execution speeds due to Lua's regularity, but maintaining low-level trace compilers is notoriously difficult due to brittle CPU architectures and complex stack reconstruction during bailouts.
- [18:40](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1120s) Standard Lua achieves cross-platform portability by structuring its runtime as an emulated virtual machine written entirely in ANSI C.
- [24:44](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1484s) Dynamic typing forces compilers into generic operations and heap allocations, while whole-program type inference on dynamic languages remains an uncomputable and largely impractical solution for performance gains.
- [30:31](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1831s) Interoperability between Lua and C operates through direct C function pointers and bytecode arrays, enabling seamless, recursive cross-boundary execution without breaking call frames.
- [32:48](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1968s) An unconfigured Lua state contains zero default standard libraries or system hooks, providing an ideal sandbox to secure runtime environments, financial software, and hardware controllers.
- [37:05](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2225s) Language committees tend to destroy conceptual integrity because members fiercely advocate for pet features while expending little effort to block language bloat.
- [40:08](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2408s) Booleans were introduced to Lua strictly because tables needed a falsy sentinel distinct from nil to avoid unintentionally deleting table entries.
- [45:10](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2710s) Zero-based indexing is not an inherent mathematical ideal for arrays, but an artifact of C dereferencing memory offsets via pointer arithmetic.
- [51:17](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3077s) If AI continues to generate code, simple and transparent programming languages will become more vital than ever so that humans can reliably verify program semantics and avoid hidden side effects.
- [56:33](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3393s) Lua's conceptual coherence was preserved because the core development team operated in academia without commercial client deadlines forcing premature feature adoption.
- [01:00:10](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3610s) Studying foundational languages like Haskell, minimal assembly, Scheme, and SNOBOL builds broader architectural imagination and counteracts the design homogenization of modern mainstream ecosystems.
## Detailed walkthrough
### [00:00](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=0s) Introduction and the Nature of Lua
Roberto Ierusalimschy previews provocative perspectives on software engineering, noting that with current advances, “It's completely feasible that you won't have programming languages,” and observing that with AI's rapid pace, “If you really think about that, it's really difficult to recommend people learning anything.” He also cites C++ and JavaScript as prime examples of runaway complexity, joking that *JavaScript: The Good Parts* is notoriously thin compared to official reference manuals.
```mermaid
flowchart LR
accTitle: Embedding vs Extending Architecture
accDescr: Diagram contrasting how traditional scripting languages extend C versus how Lua embeds inside a C host application.
subgraph Extending["Extending (e.g. Traditional Scripting)"]
direction LR
ScriptHost["Scripting Language (Main Loop)"] -->|"calls into"| CLib["C/C++ Libraries"]
end
subgraph Embedding["Embedding (Lua Architecture)"]
direction LR
CHost["C/C++ Program (Main Loop)"] -->|"drives & invokes"| LuaEngine["Lua (Dynamic Tasks / Logic)"]
LuaEngine -->|"returns control to"| CHost
end
```
Ryan Peterman introduces Lua's growing profile: by 2023, it was the second-highest programming language by contributor growth on GitHub, powering widely known systems including *World of Warcraft* and Roblox. Asked what sets the language apart, Roberto explains that Lua is fundamentally designed to be combined with systems languages such as C or C++. In this architecture, Lua manages dynamic, frequently modified, less resource-heavy tasks, while C or C++ handles computationally intensive operations.
Roberto emphasizes a common industry confusion: people frequently conflate dynamic languages with scripting languages, treating the terms as interchangeable. In practice, they serve distinct operational roles depending on who owns the main execution loop:
- **Extending:** Many conventional scripting languages operate as the outer application; the developer writes the main loop in the scripting language and uses C merely to build performance-critical extension libraries.
- **Embedding:** The application's main loop remains entirely in C/C++, which retains control and calls into the scripting language periodically.
Lua is purpose-built for the embedding model. In game engines, keeping the main loop in C is critical for enforcing frame rate rhythms: the host program ticks every frame, calls Lua to update game state, entities, and behaviors, and immediately takes control back to perform rendering and hardware synchronization. While many languages excel solely at extending, Roberto underlines: “Lua is really good for both embedding and extending.”
---
[04:05](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=245s) Designing a Language as a Library
When Peterman asks why Lua lends itself naturally to embedding while Python leans toward extending, Roberto points to its inception: “Since the beginning Lua was written as a library.” Unlike runtimes designed around an omnipresent engine executable, Lua's standalone command-line interpreter is simply an ordinary client built on top of the public API, possessing no special internal privileges.
```mermaid
flowchart TD
accTitle: Lua Execution and Isolation Model
accDescr: Flowchart illustrating how C host programs manage isolated Lua states and use load instead of eval.
CProg["C Host Application"] -->|"lua_newstate()"| State1["Independent Lua State A"]
CProg -->|"lua_newstate()"| State2["Independent Lua State B (Isolated)"]
subgraph Execution["Compilation vs Invocation"]
direction TB
CodeStr["Source String"] -->|"load(chunk)"| CompiledFunc["Compiled Lua Function"]
CompiledFunc -->|"call()"| Execute["Runtime Execution"]
end
State1 -.-> Execution
CProg -->|"lua_close()"| FreeMem["Release All Lua Resources"]
```
Designing an entire language as a library leads to deliberate architectural differences:
- **No global engine state:** Lua avoids process-wide globals. A C program must explicitly initialize a `lua_State`, and can instantiate multiple independent states that share nothing and cannot interfere with one another.
- **Deterministic resource reclamation:** The host program can shut down a state whenever it finishes a task, instantly freeing all memory and resources allocated by Lua while the C program continues running uninterrupted.
- **Cross-language exception handling:** Lua's runtime cleanly handles exceptions thrown in Lua and caught in C, and vice versa.
- **`load` instead of `eval`:** Dynamic languages typically provide an `eval` primitive that compiles and executes text immediately. Lua provides `load`, which compiles text into a callable internal function without evaluating it right away. This separation enables a C host to compile code once, inspect syntax errors up front, store the compiled handle, and invoke it repeatedly across application cycles.
---
[08:35](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=515s) Lua vs. Python: Philosophy and Performance
Comparing Lua with Python highlights fundamentally different engineering philosophies:
- **Python:** Tailored for fast standalone development with an expansive standard library. It comes batteries-included with built-in modules for nearly any conceivable utility, resulting in a large binary and runtime footprint.
- **Lua:** Intentionally minimalistic. It avoids shipping extensive out-of-the-box utility libraries because it is designed to be embedded in an application that already supplies its own domain-specific APIs (such as character movement or audio calls in a game engine).
Addressing why benchmarks show Lua running significantly faster than Python, Roberto highlights two architectural factors:
- **Performance-conscious feature design:** Lua prioritizes runtime speed over arbitrary dynamism. The team deliberately declines language features that cannot be implemented efficiently. Python favors maximal syntactic and operational flexibility, introducing layers of dynamic indirection where expressions can be continuously re-evaluated and overridden.
- **Virtual machine footprint:** Lua's runtime engine is small enough that the core virtual machine loop fits directly inside the CPU instruction and data caches, creating an inherent hardware-level speed advantage.
Peterman clarifies that dynamic languages represent a superset, with genuine scripting languages being a specialized subset, noting that JavaScript constitutes a dynamic language rather than a traditional scripting language. Roberto agrees, recalling that the concept of scripting originally emerged from Unix shells such as bash, where the language's primary purpose was coordinating and orchestrating external programs rather than running an entire software ecosystem within its own runtime.
[12:22](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=742s) The Concept of Scripting and Book Recommendations
Roberto Ierusalimschy elaborates on the foundational identity of scripting languages. At its core, scripting implies a dual-language architecture: one language serves as the coordinator while another does the heavy lifting. In a shell environment, for example, the shell language itself is primarily valuable because it controls a multitude of underlying programs written in C. “Scripting means it's like it is it's the name scripting. It means you it's like a the you coordinator. You you give a script to be executed by those other other things,” Roberto observes, defining the essential role of the script as orchestrating other components rather than running completely isolated systems.
```mermaid
flowchart TD
accTitle: Dual-Language Architecture
accDescr: Diagram showing how a scripting language coordinates underlying systems and libraries.
subgraph Coordination["Coordination Layer"]
A["Scripting Language<br>(e.g., Shell, Lua)"]
end
subgraph Execution["Execution Layer"]
B["C Programs / Engines"]
C["System Libraries"]
D["Native Extensions"]
end
A -->|"orchestrates / coordinates"| B
A -->|"calls into"| C
A -->|"binds with"| D
```
When Ryan Peterman asks about books Roberto recommends for learning language design, Roberto emphasizes his preference for accounts written directly by language creators explaining their rationale: “I think the best books are those written by the author of a language about the design of that language.” He points specifically to Douglas Crockford’s *JavaScript: The Good Parts*. While acknowledging that the title has become a running joke—given how thin it is compared to exhaustive, encyclopedic JavaScript references—Roberto respects the book's candid breakdown of what went wrong, what worked, and why certain trade-offs were made.
[14:20](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=860s) How LuaJIT Works and Why JIT Compilers Are Difficult
Addressing the performance of Lua and the existence of LuaJIT, Roberto clarifies that LuaJIT is an entirely distinct, independent project created by Mike Pall, separate from the core Lua team. However, Roberto notes that LuaJIT achieves astonishing speed precisely because of Lua's design: it is an exceptionally regular, simple language lacking excessive indirections, corner-case exceptions, or hyper-dynamic behaviors where basic operations can unpredictably morph into something else. Roberto praises Mike Pall’s engineering as unbelievable, but admits he has no desire to write JITs himself due to the frustration of low-level machine dependencies. Compilers targeting raw architectures suffer from constant churn, unstable application binary interfaces (ABIs), and strict, shifting stack-alignment requirements. Mike Pall tackled this by inventing a pseudo-assembler to bridge differences across processor architectures, yet Roberto maintains that while machine architecture is fun to study, it is exhausting to maintain.
Roberto then details the inner mechanics of LuaJIT’s trace compiler. Unlike standard JIT compilers that compile entire methods or functions, a trace compiler monitors running bytecode to identify frequently executed paths (hot loops). Once detected, it records an execution trace encompassing all operations—including inlined function calls—until the loop completes. It generates hyper-optimized machine code tailored to the exact data types encountered during recording, such as assuming an operand will always remain an integer:
```mermaid
flowchart TD
accTitle: Trace Compilation and Bailout Pipeline
accDescr: Pipeline of trace compilation showing path detection, trace recording, native execution, guard failure, and interpreter bailout.
A["Interpreter Loop"] --> B["Detect Hot Loop"]
B --> C["Record Execution Trace<br>(Inline function calls)"]
C --> D["Compile Optimized Machine Code<br>(Assumes observed types)"]
D --> E{"Guard Checks Pass?"}
E -->|"Yes"| F["Execute Fast Native Loop"]
F --> E
E -->|"No (Type mismatch / side exit)"| G["Bailout to Interpreter"]
G --> H["Reconstruct Inlined Call Stack"]
H --> A
```
The real agony of trace compilation emerges when an assumption fails. If a loop optimized for integers suddenly receives a floating-point value, a guard check fails, forcing a bailout back to the bytecode interpreter. Roberto explains that transferring state out of compressed machine registers back into the interpreter is extraordinarily painful: because calls were inlined into flat machine instructions, the physical runtime call stack was never created in the first place, meaning the compiler must synthesize and reconstruct the exact interpreter stack on the fly. Roberto quips about the sheer complexity of handling such deoptimizations: “Even to think about that I have headaches.”
[18:40](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1120s) Virtual Machine Interpretation and Ahead-of-Time Compilation
Standard Lua approaches portability entirely through standard C. Rather than translating Lua directly to native CPU instructions, the Lua engine acts as an emulated CPU written in C. When Lua source code is loaded, it undergoes a lightweight pre-compilation pass into an internal stream of bytecodes. The core runtime is structured as an interpreter loop with a dispatch switch statement. It reads an instruction, moves virtual registers, or adjusts its program counter to jump across the bytecode array, executing the logic in software. Because this interpreter loop is standard ANSI C, it runs anywhere a conforming C compiler exists—whether Linux, macOS, or x86 hardware. Roberto sums it up simply: “The all portability of Lua comes on top of portability of C.”
Regarding the performance differences between Ahead-of-Time (AOT) compilation, Just-in-Time (JIT) compilation, and pure interpretation:
- Any form of compilation typically yields a 10× to 100× speed improvement over naive interpretation, with 10× serving as a reliable benchmark.
- Trace compilers particularly excel in synthetic benchmarks because repetitive, uniform loops allow the recorded traces to run uninterrupted without failing guard conditions.
- For real-world codebases with shifting branches and varied types, neither JIT nor AOT is an absolute winner; performance depends heavily on the specific workload.
- True AOT compilers for Lua exist primarily in research and academia. Roberto mentions a master's thesis by one of his students that took Lua opcodes, expanded them directly into equivalent C source code, and fed that output into an optimizing C compiler, achieving an effortless 3× to 5× boost.
[23:21](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1401s) Compiled vs. Interpreted: The Role of Dynamic `eval`
Ryan notes that the boundary between compiled and interpreted languages is largely an artifact of the toolchain rather than inherent to the code itself; one could theoretically write an AOT compiler for Python or an interpreter for C. Roberto agrees, pointing out that C interpreters indeed exist, but stresses the fundamental reason dynamic languages remain predominantly interpreted: dynamic code evaluation.
“One of the hallmarks of interpreted languages is the eval. So, if you want to compile the language and keep eval, you have to have a compiler as a library of your of your runtime because you may want to compile things during execution,” Roberto explains. Because dynamic languages allow programs to construct, inspect, and evaluate arbitrary code strings during runtime, the toolchain must bundle a full compiler inside the deployed runtime binary if it wants to stay compiled. This overhead and structural complexity explain why dynamic languages lean heavily on interpreters, while compiled variants of dynamic languages frequently choose to drop or restrict runtime `eval` support altogether.
[24:44](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1484s) The Role of Type Systems in Compilation and Inference
Roberto continues discussing compilation limitations, noting that dynamic languages can compile only under certain constraints—such as forbidding `eval`—while C code can theoretically be interpreted, albeit extremely slowly. Ryan asks about the fundamental role that type systems and annotations play in the compilation process. Roberto explains that a compiler's job becomes vastly simpler when working within an appropriate type system. In C, for example, knowing whether a variable is an integer, a float, or a double informs the compiler of the exact byte count required in memory. Without that upfront knowledge, dynamic languages are forced to dynamically allocate nearly everything on the heap, triggering massive performance penalties.
```mermaid
flowchart TD
accTitle: Static vs Dynamic Addition Compilation
accDescr: Comparison of how static languages emit machine code directly versus dynamic languages checking types at runtime.
subgraph Static ["Static Typing (e.g., C)"]
S1["Source: A + B"] --> S2["Compile Time: Types known (int + int)"]
S2 --> S3["Direct Machine Code: ADD instruction"]
end
subgraph Dynamic ["Dynamic Typing (e.g., Lua)"]
D1["Source: A + B"] --> D2["Compile Time: Emit generic OP_ADD bytecode"]
D2 --> D3{"Runtime Type Check: What are A & B?"}
D3 -->|"Int + Float"| D4["Convert & Add"]
D3 -->|"String + String"| D5["Concatenate or Error"]
end
```
The difference becomes glaringly obvious in simple operations like `A + B`. In a statically typed language, the compiler knows the operand types at compile time and directly generates the specific machine instruction to add two integers. In a dynamic language, the virtual language compiler emits a generic addition bytecode, and the runtime must inspect what `A` and `B` are on the fly—converting an integer to a float, concatenating strings, or invoking custom methods. Roberto points out that languages like TypeScript do not help native compilation in this way because their types are structural and erased: “So, then it's impossible to use them to compile because oh, probably will be an integer, but if it's not I mean it's if it I have an integer register to put 20 it must be a register or things will not work.” When Ryan asks if type inference could unambiguously reconstruct types to compile Lua ahead of time, Roberto answers bluntly:
> “Nope. I mean that's not computable, but a lot of people tried to to do in type inference for dynamic languages and it's really hard pro problem and it's very difficult to do anything useful in terms of performance.”
- Type inference on dynamic code tends to collapse into overly generic types, preventing low-level machine optimizations.
- Even without enforced types, developers using tools like LuaJIT achieve substantially higher performance by practicing internal type discipline—refraining from reusing a single variable for both integers and strings.
[30:31](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1831s) Cross-Language Calls: How Lua and C Interoperate
Ryan transitions to cross-language execution, asking how compilers, interpreters, and runtime stacks coordinate when Lua and C invoke each other. Roberto explains that the core mechanism for Lua calling into C relies on standard C function pointers. When initializing a Lua state from C, the host program registers function pointers from its C library under string names inside Lua's internal data structures. During interpretation, whenever Lua encounters an instruction like `call A`, it inspects `A`, recognizes it as a C function pointer, and executes the native address directly.
Conversely, when C wants to call Lua, it views the Lua function simply as a data structure—an array of bytecode opcodes. C invokes the Lua interpreter function, passing that structure to be evaluated. This interoperability nests seamlessly across call frames: a Lua script can call a C function, which in turn calls another Lua function, recursing back and forth across language boundaries on the stack without breaking execution invariants.
[32:48](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=1968s) Sandboxing, Safety, and Scripting Python with Lua
Ryan asks about the security and hardware-protection benefits of scripting languages that Roberto has highlighted in previous talks. Roberto recounts an anecdote from an appearance in Brazil, where he was invited to deliver a presentation at a Python conference (PyCon). During the talk, Roberto emphasized Lua's dual-language architecture and its history of being embedded inside C, C++, and Fortran. To his surprise, an attendee revealed that their company used Lua to script a massive financial software suite written entirely in Python.
The team needed an interactive command line to inspect and debug running financial transactions, but exposing an embedded Python REPL created an existential security risk. Because Python affords unrestricted access to program internals, an arbitrary Python snippet could mutate critical application state, break core invariants, or corrupt transaction files. Instead, they embedded a Lua state within the Python environment to act as a secure execution layer.
```mermaid
flowchart LR
accTitle: Lua Sandboxing Layer
accDescr: Showing how Lua isolates user input from direct C or Python internals via an explicit whitelist.
By default, an initialized Lua state is completely bare:
> “When you create a Lua state, it has no functions at all. There is nothing. I mean, you cannot open files.”
Host applications must explicitly expose whitelisted functions—standard libraries for math, I/O, or custom domain logic—via function pointers. The financial team used this restriction to constrain user scripts strictly to safe, exposed actions. Roberto notes that hardware systems utilize the exact same mechanism to protect physical components. If an untrusted script had direct memory or port access to the fan speed controller regulating CPU temperature, it could throttle the fan and physically burn out the processor; sandwiching the execution inside an isolated Lua sandbox ensures every parameter is validated before touching the hardware. As Ryan notes, this architecture functions as a true sandbox environment, which Roberto confirms: “Yes, exactly. Yes, sandbox is the exact word for that.”
[37:05](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2225s) Design Committees and Conceptual Integrity
Ryan Peterman brings up a quote referencing an old joke that “a camel is a horse designed by a committee,” asking Roberto Ierusalimschy whether he believes the best programming languages are designed by as few people as possible. Roberto agrees wholeheartedly, pointing out the inherent human dynamics that plague language committees. In a committee setting, every participant arrives with their own pet features and fights relentlessly to embed them into the language. Conversely, almost nobody expends political capital to fight against adding unnecessary complexity. Because individuals fight far more fiercely to include their ideas than to exclude those of others, committees inevitably fall into a cycle of endlessly accumulating features. Often, members do not even fully understand or know every corner of the language they are expanding, leading to parts that fail to integrate cleanly with one another.
This dynamic threatens what Roberto calls “conceptual integrity”—the principle that every part of a language fits harmoniously together and is designed with the whole system in mind. Roberto acknowledges that Lua has its share of flaws and mistakes accrued over time, but he argues that a small, tight-knit group where everyone understands what everyone else is thinking makes it far easier to achieve coherence. Crucially, a small team preserves the freedom to reverse decisions. Leaving a feature out allows designers to change their minds and add it later once its necessity is proven, whereas incorporating a feature prematurely makes it nearly impossible to remove without breaking code. For Lua, the guiding philosophy when facing uncertainty is strict: “When in doubt, our default is always don't put it.”
- When languages are built by committee, members push vigorously for their own proposals while offering weak resistance to bloat, resulting in disjointed designs.
- Conceptual integrity relies on a small core group maintaining a shared mental model of the entire language.
- Reversing an omission is cheap because features can always be introduced in future releases; retracting an existing feature creates painful breaking changes.
---
[40:08](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2408s) The Absence and Eventual Addition of Booleans
Ryan asks why the original version of Lua lacked a dedicated Boolean type, noting that this is unusual compared to most modern languages. Roberto points out that historically, languages such as original C, C90 (up until C99), and Lisp functioned perfectly well without distinct Booleans by using numbers or other conventions, where zero represented false and any non-zero value represented true. In fact, Roberto reveals that Booleans were only added to Lua because they specifically needed a representation for `false`, quipping that “`true` is completely useless in Lua” because almost any non-false value can serve that purpose.
The necessity arose directly from Lua's table architecture. In Lua, an associative array treats a key mapped to `nil` as completely indistinguishable from an absent key; storing `nil` at a key deletes it from the table, and looking up an absent key returns `nil`. This created a dilemma when programmers needed to store an explicit negative or deactivated state inside a table: storing `nil` erased the entry altogether. To differentiate an existing key holding a negative value from a missing key, Lua required an explicit `false` value. Having introduced `false`, the designers included `true` alongside it for symmetry, even though it was practically redundant given the language's existing truthiness rules.
```mermaid
flowchart TD
accTitle: Lua Table Value Representation
accDescr: Diagram showing how Lua distinguishes between missing table keys and explicit false values.
subgraph TableLookup["Table Entry Evaluation"]
Key["table[k]"]
Absent["Absent Key"]
ExplicitFalse["Explicit Key with false"]
NilValue["Evaluates to nil"]
FalseValue["Evaluates to false"]
end
- In original C through C90, integers served as truth values: zero signified false, and non-zero values signified true.
- In Lua tables, key absence and the value `nil` are semantically identical, making it impossible to use `nil` as a persistent false flag.
- Introducing `false` resolved the absence ambiguity by providing a distinct falsy sentinel that could reside inside a table without deleting the key.
---
[43:22](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2602s) Truthiness, Flexibility, and Implicit Semantics
When asked why Lua did not simply adopt zero as `false` rather than inventing a Boolean type, Roberto explains that doing so later in the language's life would have introduced an intolerable backward incompatibility. He observes how different dynamic languages handle truthiness: Python classifies zero, empty lists, and various empty collections as falsy, while JavaScript pushes implicit coercion even further, producing behaviors that can be notoriously erratic.
Roberto reflects on the universal engineering trade-off at the heart of language design: “The more flexibility you have, the less protection you have.” While dynamic truthiness permits compact, convenient code, it also increases the risk of subtle bugs. Even statically typed languages like C carry implicit flexibility under the hood. In C, conditionals can directly evaluate integers, floating-point numbers, or pointers because the compiler generates an implicit test against zero (or the null pointer, which is represented as a magic zero). Being forced to be more explicit generally prevents programming errors, leaving designers to balance conciseness and ease of writing against error resistance.
- Modifying existing zero semantics in Lua would have broken vast amounts of legacy code across the ecosystem.
- Python treats multiple empty data structures and numbers as falsy, whereas JavaScript contains even more aggressive implicit type conversions.
- C features implicit boolean testing across integers, floats, and pointers by translating conditions into comparisons against zero or null.
---
[45:10](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2710s) One-Based Indexing and the Illusion of Arrays
Addressing one of Lua's most distinctive and widely debated features, Ryan asks why Lua uses 1-based indexing instead of 0-based indexing. Roberto points out that outside the narrow tradition of C-derived programming languages, the entire world is 1-indexed. Books begin with chapter one rather than chapter zero, and mathematicians define sequences as $A_1, A_2, A_3$ and matrix elements as $A_{1,1}$. Historically, languages like Fortran used 1-based indexing, while Pascal allowed arbitrary bounds, such as indexing an array from $-5$ to $5$.
Roberto explains that zero-based indexing only achieved ubiquity because modern languages unthinkingly mimicked C's syntax and operators. Crucially, C does not actually feature true array indexing; it relies on pointer arithmetic. In C, writing `A[i]` is merely syntactic sugar for dereferencing `*(A + i)`. Because the index represents a memory offset or displacement from the base pointer address rather than an ordinal position, the very first element naturally sits at an offset of zero. Subsequent languages lacking pointer arithmetic copied C’s zero-based indexing anyway, confusing an implementation artifact with a natural mental model.
```mermaid
flowchart LR
accTitle: C Offset vs Lua Ordinal Indexing
accDescr: Comparison between C pointer arithmetic offsets and Lua 1-based ordinal indexing.
subgraph CModel["C Pointer Arithmetic (Offset Model)"]
Base["Base Address (A)"]
Offset0["*(A + 0) -> First Element"]
Offset1["*(A + 1) -> Second Element"]
Base --> Offset0
Base --> Offset1
end
Roberto acknowledges that zero-based indexing holds minor mathematical advantages for specific algorithms, such as calculating circular buffer positions with modulo arithmetic. However, Lua was explicitly created with end-user programming in mind. Explaining to a non-programmer or a twelve-year-old child that the "first" item in a list is located at index zero is unnatural and confusing. Because professional software engineers can easily adapt to any indexing convention, Lua deliberately prioritized usability for non-programmers: “We always joke it's much easier to make life easier for the non-programmers.”
- Real-world numbering systems, matrix math, and legacy languages like Fortran and Pascal operate on 1-based indexing or user-defined ranges.
- Zero-based indexing in C is an artifact of pointer arithmetic, where the index represents a byte or element offset from a base memory address.
- Lua chose 1-based indexing to keep the language accessible and intuitive for end users and non-specialists, sparing them from counterintuitive computer-hardware conventions.
[49:58](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2998s) 1-Based Indexing and Testing Bugs
Ryan asks Roberto whether Lua's 1-based indexing causes bugs when programmers assume arrays start at 0. Roberto acknowledges that off-by-one errors happen, but he argues that such mistakes are not subtle edge cases; rather, they reveal a complete lack of basic verification.
Roberto emphasizes that starting an array at 0 or 1 will fail immediately on the simplest test run possible. If a programmer writes code assuming 0-based indexing in Lua, it breaks predictably and instantly on access. Therefore, discovering this kind of flaw simply exposes poor engineering discipline rather than a deep language flaw.
> “So, if you have that kind of bug, it just shows that you didn’t test that code at all.” — Roberto Ierusalimschy
[51:17](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3077s) AI's Impact on Language Design and Code Simplicity
Ryan brings up Roberto’s talk on the hidden costs of language features and asks whether lower implementation costs driven by LLMs and AI code generation change how he evaluates feature additions. Roberto reflects on the radical uncertainties surrounding AI. At one extreme, human programming languages might become obsolete altogether within a few years if models write and verify raw machine code or intermediate representations directly.
Because predicting five years into the future is pure guesswork, Roberto notes that programming languages will only persist if humans remain in the loop to inspect and verify AI-generated output. In that scenario, language simplicity becomes even more vital. Large language models frequently leverage clever tricks, obscure language constructs, or hidden side effects without considering human readability, making clean design and transparency far more critical than before.
```mermaid
flowchart TD
accTitle: Human verification of AI code
accDescr: Flowchart contrasting pure AI execution with human inspection requirements.
subgraph Verification["Inspection & Verification"]
B --> C{"Human in the loop?"}
C -- "No" --> D["AI directly emits machine code"]
C -- "Yes" --> E["Code must be simple and transparent"]
E --> F["Zero hidden mechanisms & clear semantics"]
end
```
> “If you think about this idea of AI generating code, I think it’s even more important for the language to be simple, to be understandable, for you to be able to really understand that the code that you are seeing does what you think it should do.” — Roberto Ierusalimschy
- Speculating beyond 1 to 2 years about AI capabilities is mere guesswork.
- The real cost of language features has never been implementation effort alone; it is the cognitive burden placed on users to learn and reason about another mechanism.
- AI lacks human intuition about when an obscure trick will confuse a maintainer reading the code a month later.
[55:32](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3332s) The Simplicity Spectrum: Minimalist Calculi to C++
When Ryan asks Roberto to position languages on a spectrum from simple to complex, Roberto references the classic adage to make things "as simple as possible, but not simpler." Pushing simplicity to its absolute theoretical extreme results in formalisms like the lambda calculus or Turing machines. While conceptually minimal, they are impossible to write practical software in.
On the other end of the spectrum, where languages pile up features, footguns, and edge cases, Roberto points to C++ as the prime example of extreme, unwieldy complexity.
> “The simplest possible, but not simpler than that.” — Roberto Ierusalimschy
[56:33](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3393s) Career Advice and Developing Lua Without Pressure
Asked whether he would still recommend learning computer science given AI advancements, Roberto confesses that advising anyone on career choices right now is exceedingly difficult. A student entering university will spend three to five years before graduating, and no one can reliably predict what software engineering will look like then. While current AI excels at boilerplate and struggles with big-picture system architecture, there is no guarantee that divide will hold. He suggests pursuing programming because one genuinely loves doing it.
Reflecting on Lua's long history, Roberto attributes much of its success and aesthetic coherence to a rare luxury: the team never had external clients or commercial deadlines forcing their hand. If a feature felt dubious, they could simply postpone it for a year or leave it out entirely. When judging success, Roberto values deep appreciation from peers he respects far more than raw metrics like user numbers or follower counts.
> “We had this privilege of not having to satisfy clients.” — Roberto Ierusalimschy
> “Sometimes for me it’s much more important if someone that I really admire says something good about Lua than, ‘Oh, we have that many users.’” — Roberto Ierusalimschy
- Academic freedom shielded Lua from rushing unpolished features into the core runtime.
- Modern web metrics obsess over vanity audience numbers rather than quality of feedback.
- Enjoying the process of programming remains the best justification for learning it amid rapid AI disruption.
[01:00:10](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3610s) Languages to Make You a Better Engineer: Haskell, Low-Level Assembly, and Scheme
Ryan asks Roberto to recommend three programming languages every engineer should learn to improve their craft. Roberto immediately champions Haskell as an unmatched vehicle for mastering pure functional programming. He cites a classic joke from the community to highlight its emphasis on correctness over premature optimization:
> “In C, you spend one week to make it efficient and then you spend one year to make it correct. In Haskell, you spend one week to make it correct and then you may spend one year to make it efficient.” — Roberto Ierusalimschy
Beyond correctness, Roberto points out Haskell’s powerful type inference as an eye-opening concept: type annotations remain optional across the code while the compiler soundly infers everything.
For the second recommendation, Roberto advises learning C or a bare-bones assembly dialect (such as the legacy Intel 8080) to understand how hardware fundamentally executes instructions, avoiding modern assembly instruction sets that have grown overly convoluted. For his third choice, he turns to Scheme, praising its minimalist foundations.
- **Haskell**: Teaches functional programming, correctness-first thinking, and whole-program type inference without mandatory annotations.
- **C or classic Assembly (e.g., Intel 8080)**: Exposes the bare reality of how machines execute code without excessive modern instruction-set bloat.
- **Scheme**: Demonstrates language design elegance and clean dynamic semantics.
[01:02:22](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3742s) The Value of Older Languages and SNOBOL
Continuing his thoughts on what makes programming languages rewarding to study, Roberto highlights the beauty of an economy of ideas—systems built around very few fundamental concepts that nonetheless achieve extraordinary expressive power. Beyond minimalist classics, he points to SNOBOL as a fascinating historical artifact worth examining. As one of the earliest languages to pioneer native pattern matching and dedicated text manipulation, SNOBOL approached string processing and control flow with ideas that look radically different from contemporary paradigms.
Roberto observes that studying older languages provides an essential antidote to the homogenization of modern software design. Modern languages copy heavily from one another, converging on uniform conventions—such as universal zero-based indexing—simply because that is what everyone else does. Examining vintage languages exposes developers to alternative paradigms that broke completely different ground, broadening one's architectural imagination. As Roberto points out: “And so, it's interesting to see some older languages that have some ideas that may maybe not even good ideas, but it's very interesting.”
- **SNOBOL’s legacy:** Remembered as an early pioneer in string processing and pattern matching, demonstrating how deeply focused language primitives can shape a programming model.
- **Modern conformity:** Contemporary programming languages tend to imitate dominant design choices, leading to an intellectual uniformity across mainstream ecosystems.
---
[01:03:21](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3801s) Advice to a Younger Self: Mastery Takes Time
When Ryan asks Roberto what guidance he would pass back to his younger self at the dawn of his career, Roberto offers an uncompromising case for patience, depth, and broad learning. In an industry obsessed with speedruns and rapid upskilling, he cautions that real mastery resists shortcuts. Building genuine technical understanding demands studying a wide variety of subjects over a sustained period, and there is no substitute for putting in the foundational work.
Roberto pokes fun at the modern internet trope of rapid crash courses, where tutorials advertise teaching complex technologies in a matter of minutes. Reflecting on his own learning journey, he offers an alternative ambition: “I also joke about that people say, 'Oh, learn Lua in 30 minutes' or 'learn Python in 5 minutes,' and I always say that I wanted to learn programming in 5 years.” He stresses that there is no trick or magic formula for bypassing effort; deep competence only comes from taking the time to explore and synthesize many disparate ideas.
---
[01:04:19](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3859s) Outro and the Split Ergonomic Keyboard Project
Closing the conversation, Ryan thanks Professor Ierusalimschy for his time and insights, and Roberto expresses his shared enjoyment of the discussion. Wrapping up the episode, Ryan asks listeners to leave comments suggesting future guests, noting that legendary figures previously featured on the show—including computer scientist Barbara Liskov, database pioneer Michael Stonebraker, and distributed systems engineer Marc Brooker—were all invited as a direct result of audience recommendations.
Ryan also shares an update on his hardware endeavor: designing a custom split ergonomic keyboard. Holding up the prototype inside its travel case, he explains that the design attempts to build the exact ergonomic tool he wished existed for his own work. The keyboard launched on Kickstarter and funded successfully within its first eight hours. With the campaign moving into the long manufacturing and tooling phase, late pledges were opened for interested viewers.
## Notable quotes
> [01:00:10](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3610s) In C, you spend one week to make it efficient and then you spend one year to make it correct. In Haskell, you spend one week to make it correct and then you may spend one year to make it efficient. — Roberto Ierusalimschy
> [43:22](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=2602s) The more flexibility you have, the less protection you have. — Roberto Ierusalimschy
> [51:17](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=3077s) If you think about this idea of AI generating code, I think it’s even more important for the language to be simple, to be understandable, for you to be able to really understand that the code that you are seeing does what you think it should do. — Roberto Ierusalimschy
> [12:22](https://www.youtube.com/watch?v=jCZnFKk6M9A&t=742s) I think the best books are those written by the author of a language about the design of that language. — Roberto Ierusalimschy
## Mentioned
Roberto Ierusalimschy, Ryan Peterman, Lua, Python, C, C++, JavaScript, LuaJIT, Mike Pall, Douglas Crockford, JavaScript: The Good Parts, TypeScript, Haskell, Scheme, SNOBOL, Intel 8080, Fortran, Pascal, Roblox, World of Warcraft, Barbara Liskov, Michael Stonebraker, Marc Brooker, WorkOS, Kickstarter
## Video description
Roberto Ierusalimschy is the creator of the Lua programming language. I interviewed him about Lua's unique strengths, programming language design and predictions for how AI will impact programming languages.
• My ergonomic keyboard project I mentioned, you can follow along here: https://read.compose.llc/
• The Kickstarter page for it: https://www.kickstarter.com/projects/ryanlpeterman/compose-simple-ergonomics-beautifully-done
• WorkOS: makes your app Enterprise Ready with easy to use APIs to add SSO, SCIM, RBAC, and more in just a few lines of code, check them out at https://workos.com/
𝗧𝗶𝗺𝗲𝘀𝘁𝗮𝗺𝗽𝘀:
00:00 Intro
00:43 What sets Lua apart
08:35 Comparing Lua with Python
13:04 Top book recommendation on language design
14:20 How JIT works and why it is hard
23:21 Compiling Python and interpreting C
30:31 How cross language calls work
36:58 Lua unique design decisions
51:17 Predictions for AIs impact on languages
01:00:10 Top 3 languages to learn to become a better engineer
01:03:21 Advice for his younger self
01:04:19 Outro
• The Evolution of Lua: https://www.lua.org/doc/hopl.pdf
• LuaJIT: https://luajit.org/
• How much does it cost: https://www.youtube.com/watch?v=EUvgoxBm7uc
• JavaScript: The Good Parts (not an affiliate link): https://www.amazon.com/dp/0596517742
Roberto Ierusalimschy 面向 C 宿主嵌入设计 Lua
Lua 之所以能实现极高的运行速度和内存隔离,是因为它被特意设计为可嵌入库,而非独立的单体引擎。
Roberto Ierusalimschy 深入解析了使 Lua 区别于其他脚本语言的底层设计理念,强调其核心定位是轻量级、可嵌入的 C 语言库,而非庞大的独立运行环境。他探讨了虚拟机解释执行、LuaJIT 中的追踪编译、动态代码求值以及显式沙箱机制等方面的技术权衡。此外,Roberto 还反思了如何避免设计委员会导致的语言膨胀、从 1 开始索引的历史缘由,以及为什么 AI 代码生成需要更简单、更具可检查性的编程语言,而不是复杂的语言。
Lua 与传统脚本语言的不同之处在于,它从一开始就被构架为一个可嵌入的 C 库,而不是扩展型运行时,从而确保宿主应用程序能够完全掌控主执行循环。 (0:43)
与提前编译或标准 JIT 编译器不同,像 LuaJIT 这样的追踪编译器会跨内联函数调用记录热点循环,但当守卫失败需要重建执行栈时,会面临极大的去优化惩罚。 (14:20)
通过 eval 进行动态语言求值给提前编译带来了结构性阻碍,因为在运行时二进制文件中打包动态编译器会引入巨大的开销和复杂性。 (23:21)
Lua 采用从 1 开始的索引是一项深思熟虑的设计抉择,优先考虑了非程序员的直觉,这与源于 C 语言指针算术偏移这一实现细节的从 0 开始索引形成了对比。 (45:10)
Reading summary
Lua creator Roberto Ierusalimschy argues that genuine scripting languages are defined by their ability to coordinate systems code through embedding, rather than merely offering dynamic typing. He contends that long-term software resilience and performance depend on conceptual integrity, minimalist runtime footprints, and disciplined feature omission.
Embedding Versus Extending in Scripting Architecture (0:00)
Ierusalimschy draws a strict technical boundary between dynamic languages and genuine scripting languages, tracing the latter to Unix shell orchestration. While conventional dynamic environments like Python lean toward extending—running the outer application loop in the scripting runtime and calling down into native C extension libraries—Lua is engineered primarily as an embeddable library. In an embedded architecture, the host program in C or C++ retains ownership of the execution loop, invoking Lua selectively for reconfigurable game logic or financial transaction rules before immediately reclaiming control for hardware synchronization and rendering.
Designing Lua as a stateless C library yields critical structural advantages. The runtime avoids process-wide globals, allowing host applications to instantiate completely isolated states via API handles and free all allocated resources deterministically upon task completion. Unlike typical dynamic languages that rely on immediate eval primitives, Lua separates compilation from evaluation through load, enabling systems hosts to compile chunks once, catch syntax errors upfront, and execute the resulting function pointers repeatedly across application cycles.
Runtime Footprints, Types, and JIT Trade-Offs (8:35)
Lua's execution speed stems from deliberate architectural economy rather than complex compilation toolchains. The core virtual machine interpreter is intentionally compact enough to reside entirely within CPU instruction and data caches, avoiding the indirection and large standard library footprints typical of Python. While independent projects like Mike Pall's LuaJIT achieve dramatic 10× to 100× performance gains via trace compilation, Ierusalimschy notes that JIT engines require intense maintenance to handle processor-level ABI shifts and painful bailouts, where inlined machine execution must synthesize interpreter call stacks on the fly when guard assertions fail.
Addressing the mechanics of compilation, Ierusalimschy stresses that dynamic languages cannot simply be compiled ahead of time without severe performance penalties or the loss of features like runtime eval. Because dynamic typing defers operand inspection to runtime, operations like simple additions emit generic bytecodes that require dynamic heap checks. Structural type annotations, as seen in TypeScript, do not assist native machine compilation because types are erased, while sound type inference across unconstrained dynamic code remains theoretically and practically intractable.
Sandboxing Security Through Default Isolation (32:48)
The architectural discipline of a language library doubles as a robust security boundary. Newly initialized Lua states instantiate with zero default functions, standard libraries, or file system access. Any capability given to the running script must be explicitly mapped via host-provided C function pointers. Ierusalimschy illustrates this with a financial institution that scripted a Python banking platform using an embedded Lua engine: exposing an interactive Python shell posed critical corruption risks because Python allows deep reflection into application internals, whereas an isolated Lua state restricted operators strictly to validated host functions.
The same sandboxing model protects hardware interfaces. In embedded systems, arbitrary user scripts granted raw hardware register access could alter CPU fan curves and trigger thermal damage. By mediating hardware control through Lua's restricted function-pointer layer, host systems enforce boundary checks and parameter validation prior to executing critical native instructions.
Conceptual Integrity and the Cost of Features (37:05)
Ierusalimschy warns against language design by committee, citing how committee dynamics encourage feature accumulation because members push aggressively for personal proposals while rarely expending political capital to reject additions. Lua's development team avoided commercial client obligations, adopting a strict default of omitting disputed features. Omitting a feature allows designers to introduce it later if necessary, whereas including an ill-conceived feature creates permanent backward-compatibility debt.
This design philosophy informed Lua's most contested conventions, including 1-based indexing and explicit Booleans. Ierusalimschy clarifies that 0-based indexing in C is simply an artifact of pointer arithmetic offsets rather than an inherent truth of computer science, choosing 1-based indexing to make the language intuitive for non-programmers. Booleans were only added later because Lua tables treat nil values and absent keys as identical; introducing an explicit false value was strictly required to store inactive states without accidentally deleting hash table keys.
Language Simplicity in the Era of AI (51:17)
Regarding the rise of large language models, Ierusalimschy suggests that if human developers remain responsible for inspecting and verifying generated code, language simplicity becomes more crucial than ever. Automated code generators tend to exploit obscure syntax, complex abstractions, and implicit side effects without weighing long-term maintainability. Transparent languages with minimal hidden mechanisms lower the cognitive overhead required for engineers to audit machine output.
To develop sound engineering intuition, Ierusalimschy advises studying conceptually focused systems such as Haskell for purely functional correctness, low-level assembly or C to grasp physical hardware execution, and Scheme for minimalist syntax. He cautions against superficial crash courses, arguing that technical mastery requires sustained, multi-year study across distinct computing paradigms.
Ierusalimschy leaves open whether programming languages themselves will survive long term if artificial intelligence systems transition to generating and verifying machine representations directly without human intervention. In the interim, he maintains that the primary cost of any language feature is not the effort required to implement it, but the enduring cognitive burden it places on the human mind reasoning about the system.
精炼阅读摘要
Lua 创造者 Roberto Ierusalimschy 指出,脚本语言的核心在于作为协调层与底层系统语言配合,而 Lua 从一开始便作为库设计以实现嵌入宿主;在现代软件设计与 AI 浪潮下,保持语言的概念完整性与极致简约比盲目堆砌特性更为关键。
脚本语言的协调本质与嵌入式架构 (0:00)
行业中常将动态语言与脚本语言混为一谈,但两者的运行时控制权归属截然不同。许多传统脚本语言采取“扩展”(Extending)模式,主循环位于脚本语言中,C 仅作为构建高性能扩展库的辅助工具;而“嵌入”(Embedding)模式则将应用的主执行循环完全保留在 C 或 C++ 中,由宿主定期调用脚本语言以处理动态、易变且资源开销较小的任务。根据主持人 Ryan Peterman 提及的数据,到 2023 年 Lua 在 GitHub 贡献者增速上排名第二,广泛应用于《魔兽世界》和 Roblox 等系统。在游戏引擎中,由 C 保持主循环对于维持固定帧率至关重要,宿主每帧驱动状态更新后立即回收控制权以完成渲染与硬件同步。
Roberto 解释称,脚本(Scripting)的概念最初源自 Unix shell 等工具,其核心使命是编排和调度外部的 C 程序,即构建一种“协调者与执行者”的双语言架构。Python 等语言因自带庞大的标准库而偏向快速独立开发与扩展模式,而 Lua 自诞生起就被设计为一个纯粹的库。Lua 的独立命令行解释器本身只是基于公共 API 构建的普通客户端,没有全局引擎状态,C 宿主可随意初始化完全隔离的 lua_State,并在任务结束后立刻释放所有关联资源。此外,Lua 提供将文本编译为可调用内部函数的 load 原语而非直接执行的 eval,使得宿主能预先检查语法、缓存函数句柄并在后续生命周期中反复调用。
性能权衡、JIT 追踪与类型系统限制 (14:20)
在基准测试中,Lua 的执行性能明显快于 Python。Roberto 将其归结为两大架构因素:一是 Lua 拒绝引入任何无法高效实现的动态特性,而 Python 具备深层的动态间接层;二是 Lua 极小的虚拟机体积使其核心解释器循环能够直接装入 CPU 的指令与数据缓存。作为独立项目的 LuaJIT 由 Mike Pall 开发,其卓越性能正是建立在 Lua 语言高度规整、缺乏极端动态行为的基础之上。LuaJIT 采用追踪编译(Trace Compilation)机制,仅记录高频循环的热路径并将内联调用直接编译为针对特定数据类型的机器码。然而,一旦运行时出现类型不匹配,保护检查(Guard Check)失败就会触发解释器回退(Bailout),系统必须在没有物理调用栈的情况下动态重建解释器栈,其实现复杂度极高。
关于动态语言是否能够提前编译(AOT),Roberto 指出动态语言普遍依赖运行时 eval 机制,若要保留该特性就必须在运行时二进制中捆绑完整编译器。此外,静态类型系统让编译器在编译期即能获知确切的内存字节大小并直接生成机器指令,而动态语言处理类似加法操作时必须在运行时检查类型并动态派发。对于是否能通过全局类型推断来提前编译 Lua,Roberto 明确表示这在计算上不可行,针对动态语言的类型推断通常会退化为过于宽泛的泛型,无法带来实质性的底层机器优化。标准 Lua 因此完全依赖 ANSI C 实现跨平台解释器循环,仅通过 C 的可移植性在各硬件平台上运行。
沙盒隔离机制与跨语言安全控制 (30:31)
跨语言调用在底层依赖明确的接口协议。C 宿主通过将 C 函数指针以字符串名称注册到 Lua 的内部数据结构中,Lua 解释器在遇到对应指令时直接跳转执行底层原生地址;反之,C 宿主将 Lua 函数视作由字节码构成的纯数据结构,调用解释器完成评估。这种机制支持双向深层嵌套,且不会破坏运行时的栈不变式。
在安全与硬件控制领域,Lua 的最小化架构构成了天然的沙盒环境。新创建的 Lua 状态默认没有任何内置函数,甚至不具备打开文件或访问系统的能力。Roberto 举例称,巴西某团队在纯 Python 构建的大型金融交易系统中嵌入 Lua 作为交互式命令行层:由于暴露原生 Python REPL 允许用户直接篡改核心状态与事务文件,他们利用纯净的 Lua 状态并仅显式暴露白名单函数指针,从而对用户输入实施边界校验。硬件控制系统同样依赖这种隔离机制,防止未受信任的脚本直接向底层端口发送指令烧毁风扇或处理器。
极简主义、委员会困境与以人为本的设计选择
关于语言的演进哲学,Roberto 赞同语言应由极少数人主导设计。委员会模式往往因每位成员争相推行自己的特性而缺乏阻击冗余的动力,最终导致特性无休止堆砌并破坏“概念完整性”。Lua 秉持“有疑问时坚决不加”的原则,因为未加入的特性随时可在未来补充,而已发布的特性一旦反悔则会引发痛苦的破坏性变更。Lua 在学术环境中孵化,没有商业交付期限与外部客户压力,这使得设计团队能从容推迟或放弃不成熟的提案。
Lua 的具体语法设计也体现了对使用者的权衡。早期 Lua 缺少独立的布尔类型,后来引入 false 仅因表(Table)结构中 nil 代表键不存在,若要存储显式的否定状态而又不删除键,就必须有一个独立的假值。至于广泛争议的 1 起始索引(1-based indexing),Roberto 解释称 C 语言的 0 索引本质上是基地址偏移量的指针算术语法糖,而现实生活、数学矩阵以及非程序员的直觉均以 1 作为序列起点。尽管 0 索引在循环缓冲区取模等算法上有微弱优势,但 Lua 的核心目标是降低非专业开发者的心智负担。Roberto 认为因假定 0 索引而导致的越界错误极其显式,只要经过最基本的测试便会立即暴露,这反映的是工程验证流程的缺失而非语言本身的缺陷。
AI 冲击下的认知成本与工程学习路径 (51:17)
面对大语言模型(LLM)与 AI 代码生成的演进,Roberto 认为短期内软件开发格局充满不确定性。若极端情况下 AI 在未来完全接管甚至绕过高级语言直接生成机器码,传统编程语言的存在根基将被削弱;但只要系统仍需要人类介入审查与验证,语言的简单性就愈发关键。AI 在编写代码时缺乏对可读性与后续维护负担的同理心,容易调用晦涩技巧或隐藏机制,因此语义透明、低心智负担的设计反而能有效降低人工审查的风险。
在工程师的成长与技术视野方面,Roberto 批评了行业中推崇“数分钟速成”的浮躁风气,强调系统性掌握编程需要长达数年的扎实积累。为了建立深刻的软件工程思维,他建议学习 Haskell 以领会纯函数式编程、正确性优先原则以及全程序类型推断;学习 C 或经典汇编语言(如 Intel 8080)以直面底层机器的真实执行过程;学习 Scheme 以体会极简主义的动态语义设计。此外,研究 SNOBOL 等历史语言有助于突破当代编程语言高度同质化的思维定势,汲取不同的架构想象力。
访谈展示了 Roberto 对系统极简主义的坚持:语言的核心价值在于控制复杂度与协调底层能力。然而,随着 AI 迅速重塑代码生成与抽象层级,编程语言最终是退化为机器生成的中间表示,还是凭借透明简单的语义继续充当人类审查系统的关键桥梁,仍是一个悬而未决的开放问题。
Source quotes
[60:10] Roberto Ierusalimschy: In C, you spend one week to make it efficient and then you spend one year to make it correct. In Haskell, you spend one week to make it correct and then you may spend one year to make it efficient.
[43:22] Roberto Ierusalimschy: The more flexibility you have, the less protection you have.
[51:17] Roberto Ierusalimschy: If you think about this idea of AI generating code, I think it’s even more important for the language to be simple, to be understandable, for you to be able to really understand that the code that you are seeing does what you think it should do.
[12:22] Roberto Ierusalimschy: I think the best books are those written by the author of a language about the design of that language.
Original transcript
[0:00] It's completely feasible that you won't
[0:03] have programming languages.
[0:04] >> This is Roberto, the creator of the Lua
[0:07] programming language, and I asked him
[0:09] all about programming language design.
[0:11] >> C++ I think is a good example of a
[0:14] language that is really, really complex.
[0:16] And JavaScript I think it's even worse.
[0:19] I always joke that the JavaScript the
[0:21] good part here
[0:22] a very thin book comparatively official
[0:26] JavaScript book.
[0:28] >> Given how [snorts] much AI has been
[0:29] progressing, would you still recommend
[0:31] people learn computer science today?
[0:34] >> If you really think about that, it's
[0:37] really difficult to recommend people
[0:38] learning anything.
[0:41] >> Here's the full episode.
[0:46] In 2023, Lua was the second highest
[0:50] programming language by growth according
[0:52] to contributors on GitHub.
[0:55] And I saw that it was also used in
[0:58] famous games like World of Warcraft and
[1:00] Roblox. And so, I wanted to ask you what
[1:03] kind of programming language is Lua and
[1:05] what sets it apart?
[1:07] >> Lua is a language that you usually use
[1:10] together with combined it with another
[1:13] language like C or C++ and then you have
[1:16] an architecture where
[1:18] Lua takes care of the more dynamic part
[1:22] of your program, things that change more
[1:24] frequently, things that are are not so
[1:29] resource intensive, the the hard parts
[1:33] you have written in C or C++. So, it's
[1:36] typically what is called a scripting
[1:38] language.
[1:39] There is a some confusion I think
[1:42] between scripting languages and dynamic
[1:44] languages. And people just I think
[1:47] consider that a dynamic language to be
[1:49] more or less the same thing as a
[1:51] scripting language.
[1:53] And they are not exactly the same thing.
[1:55] So, for instance, a big difference
[1:58] between Lua and several other scripting
[2:01] languages is that
[2:03] for scripting, you have two typical
[2:05] uses. You can have the the the that I
[2:08] call who has the main loop.
[2:11] So, you can you can have the program
[2:14] written in C
[2:16] and calling Lua
[2:18] or you have your program written in Lua
[2:20] calling C.
[2:23] And for several
[2:26] called scripting languages are actually
[2:29] you
[2:30] you don't have to, but it's much more
[2:32] easy to use if you have your program
[2:35] written in the scripting language and C
[2:39] language is only for your libraries.
[2:42] And Lua is very good fit when you want
[2:44] the reverse. You want the the the the
[2:46] program written in C and then you you
[2:51] have calls Lua from time to time. So,
[2:55] the point is that Lua is very good at
[2:57] this that you have the the the main loop
[3:00] the the the program written in C and
[3:03] then it calls Lua for some tasks or for
[3:06] whatever you you you want to do. And for
[3:09] instance, in games, this is very
[3:12] important to keep the the frame ratio of
[3:16] the game. So, for instance, you have the
[3:18] loop the loop keeps the rhythm of
[3:20] [snorts] the game and keeps everything
[3:22] and then at every frame it calls Lua
[3:25] Lua's updates all characters all images
[3:28] everything in in the game and then it
[3:31] return to C to do the renderization etc.
[3:36] So,
[3:37] it is a very good fit. But, the main
[3:39] point is that that it people sometimes
[3:41] call this the embedding
[3:44] versus extending. So, you can extend the
[3:47] scripting language with C or you can
[3:50] then can embed the scripting language
[3:52] into C.
[3:55] And so may several languages are very
[3:58] good only for extending while Lua is
[4:01] really good for both embedding and
[4:04] extending.
[4:05] >> Why is Lua good for embedding and Python
[4:08] it's almost the opposite?
[4:10] >> Because in Lua since the beginning Lua
[4:13] was thought about Lua as a library.
[4:16] Since the beginning Lua was written as a
[4:19] library. So I think that's the the main
[4:22] difference. Lua has a standalone program
[4:25] that I mean you can call Lua in your
[4:27] console, in your common line, but that
[4:30] program is just a client of the official
[4:34] client of the library following the the
[4:36] same API that any other program can use.
[4:40] So this idea of language as a library, I
[4:43] think it's not very common. It's
[4:45] something that is very particular in
[4:48] Lua.
[4:49] >> When you design a language as a library,
[4:51] what are the unique, I guess, um,
[4:54] design decisions?
[4:56] >> I think that goes
[4:59] a lot of small
[5:01] small and big details. For instance,
[5:03] your your idea of what is your global
[5:06] space or your about scopes of variables,
[5:11] about exception handling. For instance,
[5:13] it's very important that it's very
[5:15] common in Lua that you raise an
[5:17] exception in Lua but catch the exception
[5:21] in C. So all these things about how you
[5:25] do exception handling in the language.
[5:28] Another
[5:29] It's not a big deal but it's a For
[5:31] instance, most dynamic languages, that's
[5:33] almost a
[5:34] definition of a dynamic language. They
[5:36] have an eval
[5:38] function that you can give a piece of
[5:41] code and then it executes that code.
[5:44] In Lua we don't instead of eval we have
[5:47] a function that we call the load.
[5:50] That you give a piece of code and it
[5:52] returns a function
[5:55] an internal function like it was a Lua
[5:58] function that when you call that
[6:00] function, then you execute
[6:02] the the associated code. It does doesn't
[6:07] execute immediately. So, you have this
[6:09] clear separation between
[6:13] compiling like the the Lua code that you
[6:16] have and then you can do that in C. So,
[6:18] in C, you can get a piece of Lua code,
[6:21] you can compile it, check if it has
[6:23] errors, etc. Then you call that for
[6:27] instance for instance a different piece
[6:29] or you can even call several times. For
[6:31] instance, you can load functions from
[6:33] Lua into C, you keep them I mean in the
[6:36] Lua
[6:38] space and then you can call that same
[6:41] function again and again. It's not a big
[6:44] difference, of course, because for if
[6:47] you have a value, you can evaluate a
[6:49] function declaration and then returns
[6:52] that function. So, you you can If you
[6:55] have a value, you can do load. If you
[6:57] have load, you can do eval. But I think
[7:00] load it's
[7:02] it's simpler to use for that kind of
[7:05] thing that is when you think about
[7:07] libraries for instance. In Lua, you it
[7:10] doesn't have a global state. I think
[7:12] that's a I forgot that that that's a
[7:14] very important difference.
[7:17] When C starts, the first thing it has to
[7:20] do is to call create a new Lua state.
[7:25] And then everything you do, you do on
[7:28] that state.
[7:30] And that state is completely
[7:33] independent
[7:35] of everything else. So, C can for
[7:37] instance create another Lua state and
[7:40] both states are completely independent,
[7:43] completely there is no communication
[7:46] between them. And so this again shows
[7:49] that the
[7:51] And so for instance, C can use a state
[7:53] to do a lot of stuff and then it can
[7:55] close the state and all memory used by
[7:58] Lua released everything that Lua was
[8:01] using is released. For instance, if C
[8:03] doesn't need to use Lua anymore for a
[8:06] program, you release all
[8:09] all resources used by Lua and your
[8:11] program in C continues and then later it
[8:14] can again create another state, etc. So
[8:18] there's as I said there is several small
[8:21] or not so small decisions in the
[8:24] language but all the time we think about
[8:26] the language as
[8:28] is that good for embedding? Is that
[8:30] possible to to do embedding and things
[8:33] like that?
[8:35] >> When you think of the programming
[8:36] community generally, everyone is quite
[8:40] familiar with Python but not as familiar
[8:42] with Lua. I thought it might be
[8:44] interesting
[8:46] to compare the two languages. So if you
[8:48] compared Lua to Python, what are the
[8:51] pros and cons of each language design?
[8:54] >> Lua is a language that is intended to be
[8:57] used
[8:59] in in this idea for of
[9:03] scripting architecture.
[9:05] So the Lua for instance, it doesn't try
[9:08] having a lot of different libraries.
[9:11] If you have something that oh I want to
[9:13] write a a quick program for something
[9:15] that Python is much better. It has all
[9:18] the libraries you can dream about it. It
[9:20] has a lot of libraries built-in
[9:23] already into the language. So that the
[9:25] language is huge. It's I mean the the
[9:28] installation it's it's a huge so
[9:31] the
[9:32] download etc. But it
[9:35] it has exactly oh I need that. Oh, it's
[9:38] there. I need that. It's there. And Lua
[9:40] is almost the opposite. Some very
[9:42] minimalistic language because they did
[9:44] that thing embed Lua into your program.
[9:47] It doesn't use almost any resources.
[9:50] Most of the libraries, the important
[9:52] libraries that you you will use will be
[9:55] provided by the program itself. It will
[9:57] be the commands like you move a
[9:59] character or do some speech or things
[10:02] like that from the engine of of the game
[10:05] for in for instance if you think about
[10:07] games, but whatever it is. So, Lua is I
[10:10] think that's the the main big
[10:12] difference. It's They they have very
[10:14] different goals.
[10:16] >> I saw some benchmarks and I saw that Lua
[10:18] is much faster than Python. Why is that?
[10:22] >> That that
[10:23] Those benchmarks are are not exactly,
[10:27] but I think that the main point is
[10:29] exactly One things that Lua does have
[10:32] these
[10:34] focus on on performance. Again, in the
[10:37] realm of scripting languages doesn't
[10:40] want to compete with C or C++. But in
[10:43] the realm of dynamic mostly now it's
[10:46] dynamic language, not scripting
[10:48] languages. We have some focus on
[10:50] performance. So, I think this is a a
[10:53] first difference. Python is exactly is
[10:55] much more I think that it's a a decision
[10:58] that they make. Performance is not that
[11:00] important. It's more important to be
[11:01] flexible, to be easy to do whatever you
[11:04] want to do. That in Lua sometimes we do
[11:07] not put some features because we think
[11:10] that there is no way to implement that
[11:12] efficiently. But also I think some part
[11:16] of that performance difference comes
[11:17] exactly because of the the size of Lua.
[11:21] So, most of the virtual machine fits in
[11:25] your cache for instance. I think there
[11:28] is these two big things. One is that Lua
[11:31] doesn't have so many features. It's not
[11:34] so dynamic as Python. So, in Python
[11:37] there is a lot of in the interactions
[11:39] because everything can mean something
[11:41] else. In Ruby, we are a little more
[11:44] conservative on that side. And but I
[11:47] think also this thing of being small
[11:49] also makes it naturally faster.
[11:52] >> So, on the distinction between scripting
[11:55] languages and dynamic or I guess it
[11:57] seems like dynamic language is a
[11:58] superset. Scripting language is a is a
[12:01] subset of that. Am I understanding that
[12:04] JavaScript is a dynamic language, not a
[12:08] scripting language from your
[12:09] perspective?
[12:10] >> Yes, exactly. Yes, exactly. Because the
[12:13] scripting it came the the the the the
[12:16] original scripting language it was bash
[12:18] or the shells from Unix. Of this idea
[12:22] that there's a language that coordinates
[12:25] other stuff. So, for instance, it can be
[12:27] extending again, you can use But the
[12:30] this idea that you have
[12:32] two different language for instance, in
[12:34] in the shell is only useful because you
[12:37] have a lot of programs written in C
[12:41] that are controlled by shell. So, the
[12:43] scripting language has this very strong
[12:45] idea that you have this idea of a dual
[12:48] language architecture. This is So,
[12:50] scripting means it's like it is it's the
[12:52] name scripting. It means you it's like a
[12:54] the you coordinator. You you give a
[12:57] script to be executed by
[13:00] those other other things.
[13:04] >> You gave a talk a while ago and someone
[13:06] asked you a question of, you know, what
[13:09] books do you recommend for, you know,
[13:11] studying programming languages? And you
[13:13] said actually
[13:14] that you enjoyed studying the language
[13:16] design of other programming languages.
[13:18] >> I like reading books that describe the
[13:21] design of
[13:23] of the languages. I I think the
[13:26] best books are those written by the
[13:28] author of a language about the design of
[13:31] that language.
[13:32] >> What book recommendation do you think is
[13:35] best on language design?
[13:37] >> JavaScript the good parts, for instance.
[13:40] The the the it's
[13:42] kind of old now, but I think that's I
[13:44] think it's a very interesting book.
[13:47] Although I always joke that the
[13:49] JavaScript the good part
[13:51] a very thin book comparatively.
[13:54] >> Official JavaScript books like
[13:57] >> one hand of the language is the good
[13:59] parts.
[14:01] But but I I think that book is really
[14:03] interesting like because exactly it it
[14:06] discussed the language it discusses in
[14:08] the bad parts and it focus on the good
[14:10] parts but then explains why it's there,
[14:13] why it it was made that way, etc. That
[14:17] is a book that I like.
[14:20] >> When we were talking about Lua, it
[14:21] sounds like one of the things that sets
[14:23] Lua apart is the performance and how
[14:26] minimal it is.
[14:28] And when I was reading about Lua, I saw
[14:30] that there's Lua Jit. How does the Lua
[14:33] Jit work?
[14:34] >> Lua Jit
[14:35] the first thing that's completely
[14:37] different project. It doesn't have
[14:40] anything to do with us. But it's a
[14:42] incredible piece of of software that is
[14:46] is a just-in-time compiler for Lua
[14:48] that's and I think exactly one of the
[14:52] reasons
[14:53] it works so well on top of Lua is
[14:56] because of the simplicity
[14:58] of
[14:59] Lua. It's a very regular language. I
[15:01] mean it has a very as I said it doesn't
[15:03] have many exceptions or too many
[15:07] indirections or things that so it's not
[15:11] that dynamic. I mean everything can mean
[15:14] something completely different. So that
[15:17] I think that gives a very good language
[15:20] for a Jit. But the the the the the Mike
[15:22] Paul is the name of the the the guy that
[15:25] made the the in the I think he's still
[15:28] working on that on the first legit. He's
[15:31] It's unbelievable his work.
[15:33] >> What makes writing a legit difficult?
[15:36] >> The first thing that for me I don't want
[15:39] to get involved with legit is because
[15:41] it's machine dependent. It's not very
[15:44] productive. I mean, you you do a lot of
[15:46] work and it only works on that
[15:48] architecture. And then I want to running
[15:51] another architecture. But, Mike Paul he
[15:54] created a kind of
[15:57] pseudo pseudo assembler that he writes
[16:00] in this assembly and then he translates
[16:03] that assembler through real machine
[16:05] pulled of the different machines. So,
[16:08] trying to unify the different
[16:10] architectures. So, there is a a lot of
[16:12] work, but I I don't like it. I I think
[16:15] architecture I
[16:18] I like to study, but I don't like to to
[16:20] to to work with because I think it's
[16:23] very unstable. Each new version they
[16:25] change that or they change that the I
[16:28] mean they they change the ABI for
[16:30] something and then now the stack has to
[16:32] be aligned in in some very
[16:35] particular way. And so
[16:38] And [clears throat] the of course also
[16:40] to to get that performance that he gets
[16:44] he made the what's called the trace
[16:47] compiler. The idea of a trace compiler
[16:49] that instead of a function
[16:52] and compiling a function
[16:55] it could it works like a
[16:57] any legit that it tries to detect things
[17:00] that are executed frequently. And so it
[17:03] starts what's called a trace. It it gets
[17:07] recording everything that the code is
[17:10] doing including function calls, etc.
[17:14] Until it closes the loop.
[17:17] And then it compiles that loop
[17:20] including function calls, etc.
[17:22] Everything in line. It com-
[17:24] compiles that for that specific, for
[17:27] instance, so that number of happened to
[17:29] be an integer, so it compiles after the
[17:32] number will be an integer again, and
[17:35] there is a lot of checks just check if
[17:37] everything is as assumed, and then it
[17:41] executes the loop. And then this is
[17:43] very, very, very fast. But anything that
[17:46] is different, for instance, you call
[17:48] again that you call it frequently with
[17:51] integers, and suddenly you call through
[17:53] with a float, then at some part of the
[17:56] code it's it is
[17:58] it breaks the the the condition, and you
[18:01] have to to return to the interpreter
[18:04] part, and I think that's one of the
[18:07] worst parts is that you you have to
[18:09] translate the state that it's all
[18:13] compressed into registers, etc. For
[18:16] instance, you don't even have a call
[18:17] stack because you didn't
[18:20] do the call you didn't do the calls, you
[18:22] just inlined it. But then, oops,
[18:24] something went wrong. Now you have to
[18:26] continue interpreting, so you have to
[18:29] recreate the stack that you didn't
[18:31] create originally, etc. So, I mean, I
[18:34] even
[18:36] >> [laughter]
[18:36] >> even to think about that I have
[18:39] headaches.
[18:40] >> Standard Lua is portable, but to execute
[18:43] on a machine, it eventually gets
[18:45] converted into machine instructions
[18:47] somewhere. Where in the stack is it
[18:50] eventually translated to machine
[18:51] instructions?
[18:52] >> Lua is written in C. The interpreter is
[18:55] written in C, and then you so you you
[18:59] compile that into machine code, the
[19:01] interpreter, the Lua code. Then the
[19:04] point when you get some some
[19:06] some I mean, the Lua code the Lua
[19:08] interpreter code. When you get some Lua
[19:10] code,
[19:12] you do what is we call a
[19:13] pre-compilation,
[19:15] that is we we translate to an internal
[19:17] language,
[19:19] and then the interpreter is just a big
[19:21] loop.
[19:22] It's a loop with a switch. I mean,
[19:25] that's a a very big simplification, but
[19:27] that that in in general terms, a loop
[19:30] with a switch that get instruction,
[19:33] does a switch, and see all this
[19:35] instruction is the instructions are
[19:37] quite similar to a CPU. Move that, but
[19:41] then instead of creating it executes
[19:44] that structure. Move Move A to B, so it
[19:48] it does a
[19:49] move A to B and executes that, and again
[19:52] repeats, executes the next instruction.
[19:55] So, this is how a interpreter works and
[20:00] basically, so we we precompile a Lua.
[20:03] There is a compilation, but we compile
[20:05] for this virtual machine,
[20:07] and then we have this main
[20:10] interpreter loop that people call that
[20:13] just fetch each instruction. So, a jump
[20:17] is just a jump. You have an array of of
[20:20] byte codes. A jump will just go to that.
[20:23] You have a
[20:24] a counter that tells where you are in
[20:27] this array. A jump you just update that
[20:30] counter. It goes to another position of
[20:32] that array where you're going to So,
[20:34] it's like you s-
[20:36] you emulate the CPU in software. It's
[20:40] written in C,
[20:41] the the interpreter. This main loop is
[20:44] written in C.
[20:45] So, if you compile it in Linux, it will
[20:48] run in Linux or in a
[20:51] X86
[20:52] architecture, it will run in that
[20:54] architecture. If you compile that in the
[20:56] Mac, it will run in the Mac. It's a C
[20:58] program.
[21:00] >> Got it. Okay, so and that's that's the
[21:02] part that's not portable, and the C
[21:04] compiler handles that.
[21:05] >> Yes. Yes. Yes. The all portability of
[21:08] Lua comes on top of portability of C.
[21:12] >> How does JIT compare to ahead-of-time
[21:14] compilation in terms of performance?
[21:17] >> Both ahead-of-time Any kind of
[21:19] compilation is easily not easily, but it
[21:23] can be 10 times faster than interpreting
[21:26] or even 100 times faster than
[21:28] interpreting depending what you were are
[21:31] doing. But 10 times is a very good
[21:34] figure. Between ahead-of-time and
[21:39] trace compilation, then depends a lot of
[21:42] what you are doing.
[21:44] After trace compilation is particularly
[21:48] good for
[21:50] benchmarks.
[21:53] Because you're repeating it more or less
[21:55] they are very uniform. You are doing
[21:57] exactly the same thing again and again
[21:59] and again. So, then trace compilers
[22:03] shine. They I mean this is the best they
[22:05] can do.
[22:07] In real programs, that's
[22:10] depends a lot, but but but you I
[22:14] wouldn't say there is a clear winner
[22:16] between the two. I think depends a lot
[22:18] of the kind of problem.
[22:20] >> Is it possible to write an ahead-of-time
[22:23] compiler for Lua?
[22:24] >> Yes, there are there are
[22:27] several.
[22:28] None I mean I think I don't not sure if
[22:32] there is anyone on production quality,
[22:35] but for research etc. there are several.
[22:39] I I have a student who wrote one like a
[22:42] master thesis. We did
[22:45] simple Lua compiler ahead-of-time Lua
[22:48] compiler or something like that. The
[22:50] exactly because it was just
[22:52] [clears throat] it gets these opcodes
[22:54] and expands into
[22:56] into C code and then you
[22:59] send that through through a C compiler
[23:02] and then you have a head of time comp
[23:05] head of time compiler for Lua and it
[23:07] gave like three five times boosting
[23:11] performance. You very very
[23:14] as the title said it's like ridiculous
[23:17] simple compiler.
[23:21] >> I always hear there's there's compiled
[23:23] languages and there's dynamic or
[23:26] interpreted languages I guess.
[23:28] But really that distinction is in the
[23:30] tool chain not necessarily in the way
[23:33] that the symbols are laid out on the
[23:34] source code. Like I could write compiler
[23:37] or program that takes in Python code and
[23:40] then converts it into machine code and
[23:44] then I would have compiled Python.
[23:46] >> And you can interpret C code too. You
[23:48] can write an interpreter for C.
[23:51] Yes. But the the main difference is
[23:54] exactly what it's easy to For instance,
[23:56] as I said, one of the hallmarks of
[23:59] interpreted languages is the eval. So,
[24:03] if you want to compile the language and
[24:06] keep eval, you have to
[24:08] have a compiler as a library of your
[24:12] of your runtime because you may want to
[24:14] compile things
[24:16] during execution. This is the hallmark
[24:19] of a dynamic language. That's all. You
[24:21] can create code while running code. So,
[24:26] that part is that that's why
[24:30] more much more often than not
[24:35] dynamic languages are interpreted.
[24:38] But you can compile but then sometimes
[24:40] or some compilers do not handle eval at
[24:44] all. They say, "Oh, you can compile as
[24:46] long as your program doesn't have
[24:48] evals." The so there is some
[24:50] restrictions. And as I said, you can
[24:54] interpret C code I mean, but you'll be
[24:58] extremely slowly with it doesn't but
[25:02] >> I think one big difference I when I look
[25:04] at statically or you know static
[25:06] languages or compiled languages
[25:08] they they seem to have type systems in
[25:10] them or the type annotations.
[25:13] What is the role of a type system in the
[25:16] compilation process?
[25:18] >> Depends a lot of the on the type system.
[25:22] The several type systems like in C for
[25:25] instance are written to be an very
[25:28] important part of the compilation
[25:30] process. So if you have the right type
[25:33] system is much much easier to write a
[25:37] compiler.
[25:38] A trivial example is when the space for
[25:41] variables because if you know all this
[25:44] is an integer, this is a float, this is
[25:46] a double, you know exactly how many
[25:49] bytes of memory you need to that.
[25:52] Otherwise it's a massive more often than
[25:55] not you have to keep everything in the
[25:56] heap dynamically allocated and so
[26:01] huge
[26:03] penalty in performance. And for instance
[26:05] when you see it as I said a trivial you
[26:07] have an addition.
[26:09] A plus B. If you know the type of A
[26:13] A plus B, you have the type of A, the
[26:15] type of B, you have a compile time you
[26:18] know all this plus is a
[26:20] I'm adding two integers. I just generate
[26:23] the machine code to add integers and
[26:26] there everything it's fine. In a dynamic
[26:28] language as here plus
[26:30] I don't I I I compile
[26:32] to a virtual in the virtual language it
[26:35] is just piece of plus but then how do I
[26:38] at run time you have to check what is A?
[26:41] Oh, A is an integer, B is a float. Oh, I
[26:44] have to convert or B is a string or
[26:46] depending on the language you're if
[26:48] you're not doing addition you can be
[26:50] doing concatenation or you can just
[26:53] calling some and you have to do all that
[26:56] at runtime. So, types can be very, very
[26:59] important to compile efficiently, but of
[27:05] course you have to
[27:07] have a type system that
[27:10] that there are several language now like
[27:12] type type script that the
[27:14] types are kind of
[27:16] you do not guarantee that everything has
[27:19] the types you said
[27:21] they have. So, then it's impossible to
[27:24] use them to compile because oh, probably
[27:27] will be an integer, but if it's not I
[27:30] mean it's if it I have an integer
[27:33] register to put 20
[27:35] it must be a register or
[27:38] things will not work. So, but if you
[27:40] have the right type system they are
[27:43] essential for a good compilation.
[27:46] >> I know some programming languages have
[27:47] type inference, but if you did you ran
[27:50] type inference and you got a unambiguous
[27:53] set of types and then you use that in
[27:56] compilation. Like could you do that for
[27:58] Lua?
[28:00] >> Nope.
[28:03] I mean that's not computable, but a lot
[28:06] of people tried to to do
[28:10] in type inference for dynamic languages
[28:13] and it's really hard pro problem and
[28:16] it's very difficult to do anything
[28:18] useful in terms of performance. What you
[28:22] sometimes you get but you have a very
[28:25] restrict
[28:27] If you write your program in that
[28:30] specific way use for So, it's like you
[28:34] don't have the type but you write the
[28:35] program thinking about type and then if
[28:39] the program is that very particular
[28:41] format, then you can
[28:45] do type inference and everything goes
[28:47] well. Otherwise, either type inference
[28:50] doesn't work, or I mean, it works, but
[28:53] it actually it infers very generic types
[28:57] for everything, and so you cannot take
[29:00] advantage of of of the types. Because
[29:03] exactly the dynamic nature of the
[29:05] language. This This also happens if Lua
[29:08] JIT, for instance.
[29:10] You can get much much better performance
[29:13] if you have a kind of a type system in
[29:16] your in your mind. Usually, we do that,
[29:19] and you and you follow that type rules
[29:23] even without a type I mean, the compiler
[29:25] the language does not impose them on
[29:28] you, but you assume, "Oh, I'm going to
[29:30] follow those some type discipline. I'm
[29:33] not going to to to use the same variable
[29:36] to store integers and strings, etc." And
[29:39] then you can have much better results.
[29:43] >> OpenAI, [snorts]
[29:44] Anthropic, Cursor, and Vercel all use
[29:47] this product to make their lives better.
[29:49] And the problem it solves is when you're
[29:51] building SaaS or an ad product, and you
[29:54] want to sell to other companies, there's
[29:56] all these requirements you need to meet.
[29:58] There's SSL, there's SCIM, there's RBA,
[30:02] there's audit logs. These are all things
[30:03] that take time to integrate, but aren't
[30:06] the main focus of your app. WorkOS is an
[30:08] API layer that lets you meet all of
[30:10] these requirements in just a few lines
[30:12] of code. So, let's say you have a new
[30:14] SaaS product, and you want to sell to
[30:15] other
[30:17] WorkOS will solve all of these critical
[30:19] feature gaps for you.
[30:21] You can check them out at workos.com to
[30:23] learn more and get started, and I
[30:25] appreciate them for supporting my work
[30:27] and sponsoring this podcast. Earlier you
[30:30] mentioned that Lua can call C, and C
[30:33] could call Lua. And
[30:36] you mentioned we talked about the the
[30:38] compiler and the interpreter, and how do
[30:41] these pieces work together in the case
[30:43] where you're chaining different
[30:45] programming languages?
[30:47] >> Yeah, the the the main trick, I mean,
[30:49] it's not a trick, the technique
[30:52] for Lua calling C is a C pointers.
[30:57] Function pointers. There is a pointer to
[31:00] a function.
[31:01] This is part of the official C
[31:04] language. So, when you start Lua,
[31:08] suppose you are in C, yeah, as I said,
[31:11] you create a library.
[31:13] Uh all right, you create a state, a Lua
[31:15] state, and then you can register, you
[31:18] send to Lua
[31:20] C pointers, so function pointers, I
[31:22] mean, pointers to functions in your C
[31:25] library. Associated with names, Lua
[31:28] stores that in some data structure.
[31:32] Okay, so it says, "Oh, the function sign
[31:36] is associated with that pointer, etc."
[31:38] So, when it's it's doing the
[31:41] interpretation, "Oh, this instruction is
[31:44] call, instruction call. Let's see what
[31:47] it's calling. Oh, it's calling A. What
[31:49] is the value of A? Oh, it's a pointer to
[31:51] and then it calls that pointer." And
[31:54] then we do the call the the the C
[31:57] function to do that.
[31:59] And the when when C wants to call Lua, I
[32:04] I mean,
[32:04] a Lua function is just a data structure
[32:08] from the point of view of of C, just an
[32:10] array of byte codes of up codes, and
[32:14] then it's calls the Lua interpreter
[32:17] function, "Oh,
[32:19] please interpret that function for me."
[32:22] So, C can call Lua to interpret that
[32:25] function, and that function is running,
[32:27] and then can call a C function using a
[32:30] pointer, and you can have that in the
[32:33] stack several levels. So, you are
[32:35] running a Lua function that calls C, and
[32:37] C calls Lua again, and Lua calls C, and
[32:40] you have recursive stuff in that, etc.
[32:43] And everything works and it's just
[32:46] just works.
[32:48] >> In one of your talks on Lua, you
[32:50] mentioned that there's security benefits
[32:52] to using a scripting language. What are
[32:55] those benefits and how does it protect
[32:57] the hardware?
[32:58] >> It's not only hardware. Once I I I gave
[33:01] a talk here in in Brazil. I was called
[33:05] it to to give a talk in a PyCon
[33:09] a PyCon they they called the Python
[33:11] conference in Brazil. They called me for
[33:14] a talk. At the end of the talk I said,
[33:16] "Oh, Lua is we use it as a scripting
[33:18] language." As I said, emphasizing that
[33:21] thing that you have this dual language
[33:24] architecture and we I I know of case of
[33:27] Lua being used with C, with C++,
[33:30] with Fortran, with several different
[33:33] languages. And then someone in the
[33:35] audience says, "Oh, we also
[33:37] use Lua for scripting Python programs."
[33:41] And what what's that the case? They they
[33:43] they have a huge Python program
[33:45] for financial stuff that has a do a lot
[33:49] of financial transactions, etc. And they
[33:52] wanted to do script I mean to have a
[33:55] like
[33:56] a common line where you could write
[33:58] instructions to be executed by the the
[34:02] at runtime, for instance, for debugging
[34:05] or for inspecting, for
[34:07] whatever you need some kind of end-user
[34:10] programming.
[34:11] But they said exactly what I was talking
[34:14] about spaces. In Python,
[34:17] basically, if you have a common line
[34:20] then you're going to run Python, that
[34:22] Python can do whatever it wants to do
[34:27] in your program. There is no way to
[34:29] protect. So, if you gave that to the
[34:32] user, the user could do whatever it
[34:34] wanted to do.
[34:36] I mean
[34:37] for the program. And so, the program
[34:39] would break a lot of invariants of a lot
[34:41] of important stuff in the files it kept,
[34:44] etc. And so, what they did, they gave
[34:47] the thinking Lua because as I said, as
[34:50] Lua you create a state. For instance, as
[34:52] I just mentioned, you can only call C
[34:55] functions.
[34:57] That's is true in Python again, but the
[35:00] particularity You can only call C
[35:03] functions if you give the C pointer
[35:06] to Lua.
[35:07] So, have a very strict or any there
[35:10] there is very little as I said, there is
[35:12] a library. When you create a Lua state,
[35:14] it has
[35:15] no functions at all.
[35:18] There is nothing. I mean, you cannot
[35:19] open files. Everything there are we call
[35:22] standard libraries. Usually, you open
[35:26] a state and you register those standard
[35:29] libraries in Lua. So, you have the the
[35:32] minimum like you have mathematical
[35:34] functions, etc. But you can open a state
[35:37] and has no functions at all in Lua.
[35:40] It can only do things calling those
[35:43] specific functions you you you gave. So,
[35:46] they they used Lua to have this kind of
[35:49] control. You can write Lua code here,
[35:51] but that Lua code can only do very
[35:54] specific things in the Python program.
[35:57] You cannot call any Python function or I
[36:01] mean, build any kind of Python data
[36:03] structure, etc. So, was this a a nice
[36:07] example. In the case of hardware, it's
[36:09] the same thing. You cannot just go in
[36:12] the For instance, there is a a port a
[36:15] hardware port that controls the speed of
[36:18] the fan
[36:19] that that keeps the temperature of the
[36:22] CPU. And if you put that very low, you
[36:25] can really burn the CPU. I mean, because
[36:27] the CPU gets very hot. And so, we've you
[36:30] have a seat you cannot call C directly.
[36:33] You must call Bingo, and only Lua has
[36:36] access to that. So, if you can only
[36:39] program in Lua, it's a doll Lua, then
[36:41] you you check whether the the numbers
[36:44] you are giving are are precise or
[36:46] whatever it has to check, and then it
[36:49] calls
[36:51] >> It's kind of like a sandbox environment.
[36:53] >> Yes, exactly. Yes, sandbox is the exact
[36:57] word for that.
[36:58] >> I saw there's this, I guess, paper,
[37:01] maybe it was a
[37:02] article you wrote on the history of Lua
[37:05] with several other people, and I thought
[37:07] there were some interesting quotes in
[37:08] there I kind of wanted to ask you about.
[37:10] So, um one of them
[37:13] it says that there's this old joke that
[37:16] says that a camel is a horse designed by
[37:19] a committee.
[37:20] >> Yes, that's not mine. That's a real
[37:24] old joke.
[37:25] >> Does that mean that you think the best
[37:28] programming languages are designed by as
[37:31] few people as possible?
[37:33] >> Yes, I do think. Yes.
[37:35] Because there's one thing that that it's
[37:38] easy to to observe it it's is is this.
[37:41] If you have a committee
[37:44] writing a language,
[37:46] everyone wants to put something
[37:50] from themselves into the language. So,
[37:53] you have that your favorite mechanism,
[37:56] and you will fight whatever it takes to
[37:59] put your favorite mechanism into the
[38:02] language. And very few people will fight
[38:06] against
[38:08] putting anything in the language.
[38:11] I mean, some people say, "Oh, no, this
[38:13] is too much. This is too complicated."
[38:15] But people fight much more fiercely for
[38:19] to for put something they you in the
[38:21] language, then
[38:23] against putting something in the
[38:25] language. So, when you have a committee,
[38:28] the the tendency is so let's
[38:30] keep adding stuff to let's keep adding
[38:33] stuff, and people sometimes even do not
[38:36] understand. I mean, they were not sure
[38:37] if everybody there is really
[38:40] know the language
[38:43] well. Oh, well, I'm not putting that,
[38:45] but that fits with all the parts of the
[38:47] Oh, I didn't even know the language has
[38:49] this other part. I mean, so
[38:51] things sometimes do not fit together
[38:53] very well, etc. So, first you have a
[38:56] what's called a conceptual integrity.
[38:59] It's a name that give that everything
[39:02] fits together, everything
[39:04] it's designed if
[39:07] everything else in mind, etc. It's much
[39:09] easier to Yeah, I'm not saying I mean,
[39:11] Lua has several
[39:14] wrong stuff regarding because of with
[39:17] time, etc. There you make mistakes, but
[39:20] it's much easier to get things
[39:24] right if you have a small group of
[39:27] people that everybody knows what
[39:29] everybody else is thinking. I mean, you
[39:32] are deciding whether to put something in
[39:34] the language or not. It's much easier to
[39:37] change your mind
[39:39] if you do not put something, and then
[39:41] later you decide to add it to the
[39:44] language, than to add something, and
[39:47] then later you decide, "Oh, I shouldn't
[39:49] have
[39:51] that was not a good idea." So, when in
[39:54] doubt, our default is always don't put
[39:57] it. If you're not very sure, don't put
[40:00] it. We can always add that later, and so
[40:03] keep the door open through to
[40:06] change your mind.
[40:08] >> I saw in the original Lua programming
[40:11] language that there was no Boolean type,
[40:13] and I've never seen that before. Why was
[40:16] there no Boolean in the original Lua?
[40:19] >> Well, C doesn't have Booleans.
[40:22] I mean, the the the the original C and
[40:24] C90 up to C99
[40:28] using integers. If it's zero, it's
[40:30] false. If it's different from zero, it's
[40:33] true. And
[40:36] and people lived quite well with
[40:39] with that Lisp. I mean, there are
[40:41] several languages that don't have
[40:43] Booleans. And actually, we only put
[40:46] Booleans in Lua
[40:48] because we wanted false. True is
[40:51] completely useless in Lua. Nobody used
[40:54] true. And nobody such a joke, but it's
[40:56] too strong, but
[40:58] it's it's not very useful. You can use
[41:01] almost any other value for true.
[41:04] But the the the the the problem is that
[41:05] the in Lua the the
[41:08] nil, that special value nil,
[41:12] is in a table. It is equivalent to the
[41:16] key not being present.
[41:20] You all think better that was a good
[41:22] that
[41:23] decision, but it's very embedded in the
[41:26] design of the language. And it has with
[41:28] this strong concept that a table
[41:32] a key that is not present in
[41:35] in a table that is an associative array,
[41:38] it has a nil value. It's completely
[41:40] indistinguishable
[41:42] whether it's absent. I mean, absent
[41:44] means it has a nil value. Nil valent
[41:47] means it's absent. And so, sometimes you
[41:50] want to have a false value. And but you
[41:53] want to know what it is there in the
[41:55] table. And so, we needed a false. To
[41:58] have a false, you can put in the table
[42:01] and it's completely different from being
[42:04] absent. You know, there is a key and the
[42:07] key has a false value. So, we needed a a
[42:11] value. And if you're going to have a
[42:13] false, then
[42:15] we added true to maybe it would make
[42:17] sense to have false without true, but
[42:19] with true it's almost useless.
[42:22] >> Why not use zero as false if you're okay
[42:25] with one as true?
[42:27] >> It could be we could have used it, but
[42:29] that
[42:30] Among other things, that would be a a
[42:33] big change because exactly it didn't was
[42:36] that way, so we we added that late here
[42:40] in the language, so changing zero to
[42:42] false would be a very big
[42:45] incompatibility.
[42:47] But it could be for instance in in in
[42:50] Python, I think a lot of stuff are are
[42:53] false. I mean, zero is false, empty list
[42:56] is false, empty In JavaScript, I think
[42:59] it's even worse. I I don't re-
[43:02] don't recall exactly, but I mean
[43:05] This is kind of arbitrary. I mean, you
[43:07] we could have for instance
[43:10] nil, false, zero, empty string. I mean,
[43:14] it
[43:14] It just depends the kind of test that
[43:17] you use.
[43:18] It's not
[43:19] In dynamic languages, it's very easy.
[43:22] >> Python has the like you're describing
[43:24] the truthy values and the falsy values,
[43:28] or I guess they can be interpreted as
[43:30] false. And but it's it's more implicit,
[43:34] less explicit, and I know JavaScript is
[43:37] even further on that spectrum where you
[43:39] can do some really weird stuff that
[43:41] implicitly has some behavior. Do you
[43:44] think that design direction is good or
[43:47] bad?
[43:48] >> It's like a small compact, it's more
[43:52] easy to write stuff. And but you always
[43:55] have this balance in any language is
[43:57] almost anything. The more flexible you
[44:00] you
[44:01] the more flexibility you have,
[44:03] the less
[44:05] protection you have. In C, you have
[44:08] something like like that. People do not
[44:10] notice, but
[44:12] because C's typed.
[44:14] But, you can check whether an integer
[44:17] is true or false.
[44:19] You can check whether a float
[44:21] is true or false directly because again
[44:24] it checks whether the float is zero or
[44:26] different from zero. You can check
[44:29] whether a pointer is nil or different
[44:32] from nil because in C nil is a magic
[44:36] zero.
[44:37] So, it's kind of if you do not write any
[44:40] test, it has an implicit test like
[44:43] different from zero.
[44:45] And this zero can be a integer, can be a
[44:48] float, can be a pointer. So, in C also
[44:52] you have this kind of
[44:53] flexibility. It's just that you have to
[44:56] be more explicit usually.
[44:59] This
[45:00] avoids errors, but
[45:02] so this you have always this balance
[45:04] between
[45:05] easy to write and
[45:07] easy to make mistakes.
[45:10] >> I saw that
[45:11] Lua is one indexed instead of zero
[45:14] indexed, and I I had never seen a
[45:17] programming language like that in my
[45:18] experience.
[45:20] >> There was a lot of programming languages
[45:22] like that.
[45:23] >> So, yeah, why why is it one indexed?
[45:26] >> Because everything in the real world are
[45:29] one indexed. If you have a book, you
[45:31] have the the chapter one, chapter two.
[45:34] Nobody number chapters as zero, one,
[45:37] two, three. Only program- programmers
[45:40] are the only Even mathematicians, if you
[45:43] get a mathematic book, a book of
[45:45] mathematic, any sequence, it's A1 and
[45:49] A2, A3. If you get a matrix, the first
[45:52] element is A11, A12, and then A
[45:56] Everything
[45:57] is written with a Only
[46:00] in programming language that is this of
[46:02] zero.
[46:03] And the funniest part is that for
[46:06] instance Fortran
[46:07] that now it's it's very old language,
[46:10] but to index it from one. In Pascal you
[46:13] can choose I mean you write an array,
[46:16] you can say this array means index it
[46:18] from minus five to five. And so it's
[46:22] it's index it from whatever value you
[46:24] want to whatever value
[46:26] you want.
[46:28] Zero became
[46:30] extremely popular
[46:33] because of C.
[46:35] C uses zero and a lot of languages copy
[46:39] kind of inspired by by by C. They have
[46:42] the same operators, the same syntax for
[46:45] expressions.
[46:47] The Boolean
[46:48] operators for instance that is not
[46:51] standard mathematics. Almost all
[46:53] languages chose the same operator A to Z
[46:57] they are written in C.
[47:00] And so a lot of language copied C and
[47:03] then zero index it became very popular.
[47:07] And what is funny
[47:09] is that in C there is no indexing. In C
[47:13] indexing is just an illusion
[47:15] because what you have is pointer
[47:17] arithmetic.
[47:20] And C when you write A index it by I
[47:23] actually what you are saying get the
[47:25] contents of A plus Y.
[47:29] And so because in C you don't have
[47:32] indexing, you have
[47:34] this
[47:36] displacements or deltas I don't know how
[47:38] offsets.
[47:40] Then it must have index it by zero
[47:42] because of that because then the first
[47:45] element is the element that is at the
[47:46] the original address. So
[47:50] C doesn't have index indexing. So when
[47:53] you say A is index by zero, it doesn't
[47:56] index by zero because it doesn't index
[47:58] by anything. But it it have this
[48:01] illusion of indexing and then it's easy
[48:03] to think that it
[48:05] And then a lot of language that do not
[48:07] use pointer arithmetic do not have this
[48:10] restriction, do not have this semantics,
[48:13] copy it C and kept the the zero indexing
[48:17] as the thing. If you got a 12-year-old
[48:21] and try to explain to them
[48:24] the zero indexing,
[48:26] I I assure it's much much easier for
[48:29] them
[48:30] to write, "Oh, I I have a list of the
[48:32] first element." I always joke that is
[48:35] the first element is zero. And you write
[48:38] first you for one, but the first element
[48:41] one
[48:42] is not one, is zero.
[48:45] So,
[48:46] it has advantages zero for this first
[48:49] some specific operators mathematically.
[48:53] For instance, you want to do a circular
[48:55] buffer, zero is better. There are some
[48:58] small advantage, but it's much much more
[49:01] confusing.
[49:03] And as mIRC has this idea of end user
[49:05] programming,
[49:07] we always joke it's much easier to make
[49:09] life easier for the non-programmers.
[49:14] And I am sure that programmers can
[49:18] program whatever index they have to do
[49:21] because they are supposedly they are
[49:23] professional. They can learn that all
[49:26] indexing is from them to put all the end
[49:30] user the burden and of using something
[49:32] completely different from them indexing
[49:35] by zero. What does it mean the element
[49:37] index zero? They never saw and oh, are
[49:40] the chapters in the book? Yes, the first
[49:42] chapter is at zero, the second chapter
[49:44] is at one. It
[49:46] No, I'm not joking. When you try to
[49:48] explain that to non-programmer, even
[49:50] though
[49:51] they don't need to be 12
[49:53] I mean, just get anyone that is not
[49:56] an internet programmer think, "Oh, I
[49:58] have the absolute I mean, but they are
[50:00] grown-up. You are
[50:02] I mean, okay, you are a Christian. You
[50:04] are
[50:05] If zero and sure you can learn through
[50:08] truth to program if one or minus one or
[50:11] whatever it is the the base you have to
[50:14] use them. Sure.
[50:16] >> But do you think you see a lot of bugs
[50:19] cuz um
[50:20] maybe someone thinks it's zero.
[50:22] >> Unfortunately, I see some bugs.
[50:25] But as I always say, that's are the kind
[50:28] of bugs that
[50:30] just shows that you will be then do
[50:34] minimum testing.
[50:36] Because that kind of bug is not a kind
[50:39] of that kind of bug that always you
[50:43] try to anything you try to do
[50:46] in a the way.
[50:48] If you
[50:49] start from zero or start from one, you
[50:52] have a bug.
[50:53] So, the the most simple test that you
[50:56] can imagine to test anything related to
[51:00] an array, to a list, etc. And if you if
[51:03] you
[51:04] mistake zero to one, you you detect
[51:07] that. So, if you
[51:09] have that kind of bug, it just shows
[51:12] that you didn't
[51:14] test that code at all.
[51:17] >> There was this talk that you gave on the
[51:19] cost of adding uh
[51:21] language features to Lua and how there's
[51:23] a lot of hidden costs. And I think it
[51:26] today
[51:27] um implementation cost of software is
[51:29] going down due to, you know, AI code
[51:32] generation or LLMs.
[51:35] And I was wondering if that changes your
[51:36] thinking on
[51:38] um I guess the cost of adding features
[51:41] to a programming language.
[51:43] >> AI is something that
[51:45] we don't really know what is going to
[51:49] happen. If you go to an extreme
[51:53] it's com- completely feasible
[51:56] that we
[51:58] we won't have programming languages. And
[52:00] I mean
[52:01] because if you did the AI is writing all
[52:05] your code and is checking all your code
[52:07] and doing everything, in a few years
[52:09] maybe we don't need programming. AI can
[52:12] write machine code directly, it doesn't
[52:14] need programming languages. It
[52:17] It's easier for for them to compile or
[52:19] even generate the code directly. I mean,
[52:22] I don't know what is going to happen in
[52:23] a few years. So,
[52:27] I
[52:28] think it's very hard for me to
[52:31] talk anything about AI because I have
[52:35] and I think nobody have a clear idea
[52:38] what I mean, people have a maybe a clear
[52:41] idea what it happens in 1 year or 2
[52:43] years, but in 5 years, I I mean, anyone
[52:47] that says, "Oh, that's going to happen
[52:49] in 5 years." It's just
[52:51] I mean,
[52:52] it's
[52:53] just guess.
[52:54] So,
[52:56] I I think it's hard to to say anything.
[52:59] This is all because of AI. So,
[53:03] keeping the meaning thing, I mean, that
[53:06] is still have the for instance the Oh,
[53:07] why you still use programming languages?
[53:10] Because if you want the the user or the
[53:13] some
[53:14] human to be able to check the result of
[53:18] what the AI is doing etc. So, I still
[53:21] think that most of the things about
[53:24] programming language still hold. For
[53:27] instance, the the the cost of complexity
[53:29] of you understanding I mean, AI create a
[53:32] code for you and then you really
[53:34] understanding what that code does. I
[53:37] mean, it's even worse. I mean, it's much
[53:39] more important that the language should
[53:42] be clear and and has a
[53:46] not no hidden mechanisms because they I
[53:50] may use a hidden mechanism it doesn't
[53:52] have the concept oh that will be
[53:54] difficult for a human to understand that
[53:57] what is really happening here is is
[54:00] something it's a something that for
[54:02] instance oh I can use that but I'm for
[54:04] sure it's going to put a comment here
[54:07] because I'm sure that someone that reads
[54:09] that in 1 month it's not going to
[54:12] understand.
[54:14] AI doesn't have that kind of
[54:16] of
[54:17] of thinking and so it just use that that
[54:20] trick or that thing and it's so I think
[54:24] the AI if you think about this idea of
[54:26] AI generating code I think it's even
[54:28] more important for the language to be
[54:31] simple to be understandable for you to
[54:34] be able to really understand that the
[54:36] code that you are seeing does what you
[54:39] think it should it should do it it
[54:41] really understand what the code is
[54:44] doing. So this thing about simplicity I
[54:47] think it's it's very important. And
[54:50] I think
[54:52] the other part from the need maybe I may
[54:55] facilitate documentation I'm not sure if
[54:58] I need this thing about conceptual
[55:00] integrity for instance to keep things oh
[55:03] that makes sense etc. I really think
[55:06] that one of the main costs is that it
[55:09] it's the the the burden you put on the
[55:11] user to learn
[55:14] one more thing about it
[55:16] your language.
[55:17] So there's the other possibility I have
[55:20] said that implementation is the that is
[55:23] the the the part that AI can really
[55:25] help you.
[55:27] It's not a really re-
[55:31] really important cost.
[55:32] >> If you think about like a spectrum of
[55:34] simple to complex, what programming
[55:37] languages are, you know, the most simple
[55:40] ones that you think of that are easy to
[55:42] understand, less confusing side effects,
[55:45] and what programming languages are the
[55:47] most complex and have the most foot
[55:49] guns?
[55:50] >> This this famous quote comes from, I
[55:53] don't know from whom, that the simplest
[55:57] possible, but not simpler than that.
[56:00] Because you see, if you go to the
[56:02] extreme of simplicity, you could get for
[56:05] instance lambda calculus or Turing
[56:08] machines and say, "Oh, that's really
[56:10] simple." I mean, this is really simple,
[56:12] but it's completely impossible to to
[56:15] write any program in that. I mean, if
[56:18] you have really simple stuff, I think
[56:20] the extremes would be like that, but you
[56:23] don't want to be there. But for the
[56:25] other side,
[56:26] I think C++ I think is a good example of
[56:29] a language that I think it's really
[56:31] really complex.
[56:33] >> A question that comes up a lot is
[56:35] given how much AI has been progressing,
[56:38] you know, would you still recommend
[56:40] people learn computer science today?
[56:43] >> If you really think about that, it's
[56:45] really difficult to recommend people
[56:47] learning anything
[56:49] for a profession. I mean, we have no
[56:51] idea what AI will do in 5 years. If
[56:55] someone is entering university now, they
[56:58] are going to graduate in 4 years, 5
[57:00] years, or
[57:02] I think maybe three three and a half
[57:05] years or
[57:06] then
[57:07] nobody have
[57:08] any idea what it's what the world is the
[57:13] your profession will be like in in 4
[57:15] years from now. So, it's really hard to
[57:18] I mean to say, "Oh, yes, you can you
[57:20] still going to have me." Because now
[57:22] people say, "Oh, no." The the the
[57:26] manual tasks, the AI is very good, but
[57:29] it's you needed the the whole
[57:30] architecture, you needed the
[57:33] like a software engineer to do to
[57:35] see the big picture, etc. That it's now,
[57:38] but it you have no idea that that will
[57:41] be true in 4 years and or 5 years. So, I
[57:45] think I mean I like I enjoy programming.
[57:49] I I could do I mean some people say,
[57:51] "Oh, use the AI for that." I mean I
[57:52] program because I like that. But exactly
[57:55] but so if choose something that you
[57:57] like, but really for if you are really
[58:00] thinking about how am I going to live
[58:02] with that? I have no idea it's really I
[58:05] think it's a really
[58:08] hard time to
[58:11] to be choosing a profession.
[58:13] >> Yeah, I mean you've worked on Lua for
[58:15] such a long time. When you look back on
[58:17] it, what went well and what didn't go
[58:21] well?
[58:22] >> We had this privilege
[58:24] of
[58:25] not having to satisfy clients.
[58:29] So, we are the we don't have as I said
[58:31] for instance, you can choose to add some
[58:33] feature to the language because you do
[58:34] not have a pressure, "Oh, we need that
[58:36] feature that feature whatever it takes
[58:40] or etc." So, we have this privilege of,
[58:43] "Oh, we are not sure whether to put
[58:45] that, we don't put that, we can wait 1
[58:47] year or so." I think that it's it's much
[58:50] easier to do a
[58:52] a good project, a beautiful project. So,
[58:55] I'm not I'm not sure this is a
[58:57] recommendation.
[58:59] Try to work with
[59:01] without much pressure.
[59:03] People usually do not have
[59:05] this choice.
[59:07] >> What was your measure of success if
[59:09] you're working on a programming
[59:11] language? Like,
[59:12] you know, how did you know it was good?
[59:14] >> It's more important to receive sometimes
[59:17] that good people that I recognize that
[59:21] as we like the the result than a lot of
[59:25] people that I have no idea what they who
[59:28] they are etc. Like so I I
[59:31] I think this metric of quantity that is
[59:34] the standard metric nowadays in
[59:37] in the web it's got much worse that
[59:39] everything is just in the number of
[59:41] followers that me doesn't care who is
[59:44] following you just as as many people as
[59:47] possible. So sometimes for me it's much
[59:49] more important if someone that I really
[59:51] admire of thing all this that say
[59:55] something good about Lua than oh we have
[59:57] that many
[60:00] But of course it's good also to have
[60:02] some number of users and to be
[60:04] recognized but I think it says a balance
[60:08] between all of this.
[60:10] >> Do you recommend people study other
[60:11] programming languages to get better at
[60:14] programming? And so question is you know
[60:17] what are the top three programming
[60:19] languages that you think every engineer
[60:21] should learn in 2026 to kind of become
[60:24] better at programming?
[60:26] >> Haskell
[60:28] I think it's a
[60:31] it's an incredible language that it has
[60:33] everything you need to
[60:35] really learn about functional
[60:37] programming.
[60:39] I think one of the main benefits is much
[60:43] there is a old joke
[60:45] seen in the Haskell community that
[60:50] if you write a program in in
[60:53] Haskell and in C
[60:56] in C
[60:57] really spend one week
[60:59] to make it efficient
[61:02] and then you spend one year to make it
[61:04] correct.
[61:07] In Haskell you spend one week to make it
[61:10] correct and then you may spend one year
[61:13] to make it efficient.
[61:15] Again it's not my joke but
[61:18] I it has some truth not
[61:20] always true etc. but I think it gives
[61:23] the the idea. I think this is
[61:26] maybe the the the main the most
[61:28] important lesson of Haskell. But,
[61:31] Haskell has many other
[61:33] validity this thing about type
[61:35] inference, for instance, that I mean the
[61:37] entire language is built is
[61:41] types are optional everywhere, and yet
[61:44] it can do type inference for everything.
[61:47] It can infer the types correctly, etc.
[61:51] C or some
[61:53] low language I mean, you can also have a
[61:57] to learn some assembler or I mean to to
[61:59] see you. But, assemblers now are
[62:02] becoming too much complex. It would be
[62:04] to learn like the
[62:06] the 8080 assembler, very old assembler
[62:09] of a very
[62:10] but to have this exactly this idea of
[62:13] what a machine does, how it does stuff
[62:16] it exactly the basic level to have this
[62:18] understanding.
[62:20] Scheme is a language that I I like very
[62:22] much because of this exactly this
[62:25] economy of of ideas, very very few
[62:29] concepts, and it can do some amazing
[62:32] things with the
[62:34] with very few concepts. The one language
[62:36] that this is very very very old, but I
[62:40] still think it is Snowball.
[62:43] What was the first I think it was the
[62:45] first languages to have pattern matching
[62:47] and this idea of doing I mean, it's
[62:50] really strong patterns, etc. And I think
[62:54] it's sometimes it's interesting to see
[62:57] old languages because exactly nowadays a
[63:00] lot of languages tend to like I said,
[63:02] the zero indexing that people
[63:05] they copy a lot of they they tend to be
[63:08] too uniform in some aspects. And so,
[63:12] it's interesting to see some older
[63:14] languages that have some ideas that may
[63:17] maybe not even good ideas, but it's very
[63:20] interesting.
[63:21] >> Last question for you is
[63:23] you know, knowing everything you know
[63:25] now,
[63:26] if you could go back to when you had
[63:29] just started your career and give
[63:31] yourself some advice, what would you
[63:33] say?
[63:34] >> To know a lot of stuff, you really have
[63:36] to study a lot of stuff and it takes
[63:39] time it's the
[63:40] I I also joke about that people say,
[63:43] "Oh, learn Lua in 30 minutes or learn
[63:46] learn Python in 5 minutes or and I
[63:50] always say that I wanted to
[63:52] learn programming in 5 years."
[63:57] That's it.
[63:59] Really learn take our time. You have to
[64:02] learn that and that there is a lot of
[64:03] stuff
[64:04] stuff to learn. There is no magic. There
[64:06] is no
[64:07] You really have to learn I mean a lot of
[64:10] different things.
[64:12] >> Thank you so much for your time
[64:13] Professor. I really appreciate it. It
[64:15] was a lot of fun.
[64:16] >> Thank you.
[64:17] It's all my pleasure.
[64:19] >> Hey, thank you for watching this
[64:20] podcast. If you liked it and you want to
[64:22] see the show grow, please support with a
[64:24] comment or a like.
[64:26] Also, if you have any recommendations
[64:28] for people you want me to bring on,
[64:30] please drop a comment. Guests like
[64:32] Barbara Liskov, Mike Stonebraker, Mark
[64:35] Brooker, these were all people that I
[64:37] brought on because someone left a
[64:39] comment. On another note, aside from the
[64:41] podcast, I'm working on building the
[64:43] ergonomic keyboard that I wish existed.
[64:45] Here's a glance at the prototype. It's a
[64:47] split keyboard, so there's two sides.
[64:50] This is in the case. But yeah, we
[64:52] launched on Kickstarter and we hit our
[64:53] goal within 8 hours of launching. I
[64:56] really appreciate it if you were one of
[64:57] the people who grabbed one of the early
[64:59] units.
[65:00] We're now working on the long journey of
[65:02] building the tooling now. And so if you
[65:03] still want to pick one up, I've left the
[65:06] late pledges open on Kickstarter, so you
[65:08] can grab one there. I'll put a link in
[65:10] the description. Thank you again for
[65:12] watching the podcast, and I'll see you
[65:14] in the next episode.