- Updated: March 12, 2026
- 7 min read
Dynamic Typing in GNU Emacs: Tagged Pointers and Poor Man’s Inheritance Explained
GNU Emacs implements dynamic typing by combining tagged pointers with a technique called Poor Man’s Inheritance, allowing a single 64‑bit Lisp_Object to represent dozens of Lisp types while keeping memory usage and garbage‑collector overhead minimal.

Why Emacs’s Dynamic Typing Matters for Modern Developers
If you’re a software developer, Emacs user, or language‑enthusiast, you’ve probably wondered how a 40‑year‑old editor still feels snappy on today’s multi‑core machines. The secret lies in its dynamic typing implementation, a clever blend of low‑level C tricks that keep the runtime both fast and memory‑efficient. The original deep‑dive can be read at thecloudlet’s Emacs series, but this article expands on the core ideas, compares them with alternative approaches, and shows why the technique still outperforms many modern runtimes.
Emacs’s Tagged Pointer + Poor Man’s Inheritance
The Tagged Pointer Core
All heap objects in Emacs are 8‑byte aligned, which means the three least‑significant bits of any valid pointer are always zero. Emacs reclaims these bits as a type tag. A Lisp_Object therefore stores either an immediate value (e.g., a fixnum) or a pointer whose low three bits encode one of eight fundamental types:
- Cons cells
- Strings
- Vectors (the “vectorlike” tag)
- Floats
- Symbols
- Immediate integers (Int0/Int1)
- Buffers, windows, hash tables, etc., via the vectorlike tag
This design lets the interpreter decide the object’s nature with a single integer comparison—no extra memory, no hidden v‑tables.
Extending Beyond Eight Types: Poor Man’s Inheritance
Eight tags are insufficient for a full‑featured editor. Emacs solves this with a pattern often called Poor Man’s Inheritance (or struct embedding). Every “vectorlike” object begins with a common header union vectorlike_header. The header contains a size field whose high bits encode a pvec_type enumeration—over 30 concrete sub‑types such as PVEC_BUFFER, PVEC_WINDOW, PVEC_HASH_TABLE, and PVEC_SYMBOL_WITH_POS.
When the runtime sees a Lisp_Object with the vectorlike tag, it:
- Casts the pointer to
union vectorlike_header*. - Reads the
pvec_typesub‑tag. - Dispatches to the concrete struct (e.g.,
struct Lisp_Buffer) based on that sub‑tag.
Because the header is the first field, the cast is safe and the GC can trace the object correctly. This two‑level tagging (primary 3‑bit tag + secondary pvec_type) gives Emacs the flexibility of a full object‑oriented hierarchy while staying within a single 64‑bit word for the public handle.
Why This Matters for Performance
The combination yields:
- Cache‑friendly layout: Most objects fit in a single cache line, reducing pointer chasing.
- Zero‑allocation for primitives: Immediate integers and floats never allocate heap memory.
- Fast type checks: A single bit‑mask and an integer comparison replace costly virtual‑function dispatch.
- Compact GC metadata: The GC only needs to follow the vectorlike header for complex objects.
How Emacs’s Approach Stacks Up Against Alternatives
Tagged Unions (Unboxed)
Languages like C++17’s std::variant or Rust’s enum allocate the size of the largest variant inline. This guarantees O(1) access but can waste memory when most instances are small. For Emacs, a PVEC_BUFFER can be hundreds of bytes, while a simple integer needs only 8 bytes. Using a tagged union would force every integer to occupy the buffer’s full size—an unacceptable overhead for a text editor that creates millions of Lisp objects per session.
Fat Pointers
Fat pointers store a separate tag alongside the address (e.g., Go interfaces or Rust trait objects). This doubles the pointer size from 8 to 16 bytes. While it expands the type space, it also doubles the memory traffic for every reference, which hurts the tight loops of the Emacs garbage collector. Moreover, the original 1980s hardware constraints (256 KB RAM) made a 16‑byte pointer impractical. Emacs’s designers therefore preferred the 8‑byte tagged pointer plus a compact header.
LLVM’s Custom RTTI
Modern compilers such as LLVM avoid C++’s built‑in RTTI for the same performance reasons Emacs does. LLVM defines a single integer SubclassID in a base class and uses compile‑time templates to generate fast isa<T> checks. This mirrors Emacs’s pvec_type enumeration. The key difference is that LLVM operates on C++ objects with virtual tables, while Emacs stays in plain C, making the technique portable to any C compiler without extra language features.
NaN‑Boxing and Other Pointer‑Tagging Schemes
JavaScript engines (V8) and LuaJIT use the IEEE‑754 NaN space to encode pointers and small integers. This is conceptually similar to Emacs’s low‑bit tagging, but it relies on floating‑point hardware and 64‑bit double semantics. Emacs’s approach is simpler: it only needs pointer alignment guarantees, which are universally available on modern CPUs.
What This Means for Real‑World Emacs Usage
The tagged‑pointer + Poor Man’s Inheritance design directly translates into measurable benefits for developers and end‑users:
- Lightning‑fast Lisp evaluation: Type dispatch is a single bit‑mask, enabling the interpreter to execute millions of forms per second.
- Reduced memory footprint: A typical Emacs session with dozens of buffers consumes < 200 MB, far less than a comparable VM‑based editor.
- Scalable garbage collection: The GC only scans the compact vectorlike headers, keeping pause times under a few milliseconds even on large projects.
- Extensibility without bloat: New object types can be added by extending the
pvec_typeenum and embedding the header—no need to redesign the core runtime.
For SaaS platforms that embed Emacs as a scripting engine (e.g., code‑generation tools, AI‑assisted IDEs), these characteristics mean lower cloud costs and higher responsiveness. The same principles can be borrowed for custom language runtimes built on the UBOS platform overview, where developers often need a lightweight, dynamically typed layer on top of a statically typed core.
Explore More on UBOS and AI‑Powered Development
If you’re intrigued by how low‑level tricks can empower high‑level AI services, UBOS offers a suite of tools that embody similar engineering philosophies:
- AI marketing agents – leverage fast in‑memory data structures for real‑time campaign optimization.
- UBOS partner program – collaborate on building custom integrations that benefit from efficient runtime models.
- UBOS pricing plans – choose a tier that matches your performance needs.
- UBOS for startups – get started quickly with pre‑configured templates.
- UBOS solutions for SMBs – scale your services without over‑provisioning.
- Enterprise AI platform by UBOS – enterprise‑grade security and compliance.
- Web app editor on UBOS – drag‑and‑drop UI that still runs on a performant backend.
- Workflow automation studio – orchestrate complex data pipelines with minimal latency.
- UBOS templates for quick start – bootstrap projects that need fast type dispatch.
- UBOS portfolio examples – see real‑world deployments that benefit from efficient runtimes.
For AI‑centric integrations, check out the following:
- OpenAI ChatGPT integration – combine large language models with low‑overhead data handling.
- ChatGPT and Telegram integration – real‑time bot responses powered by fast dispatch.
- Telegram integration on UBOS – push notifications without sacrificing speed.
- Chroma DB integration – vector search that benefits from compact in‑memory representations.
- ElevenLabs AI voice integration – audio pipelines that need low‑latency processing.
Conclusion: A Timeless Technique for Modern Needs
GNU Emacs’s dynamic typing is a masterclass in engineering trade‑offs: by reusing pointer alignment bits and embedding a lightweight inheritance header, the editor achieves the speed of a statically typed language while retaining the flexibility of Lisp. The same principles are echoed in today’s AI platforms, from LLVM’s custom RTTI to UBOS’s high‑performance services.
“When you can encode type information in the bits you already have, you win on both memory and speed.” – Emacs core developer (paraphrased)
Whether you are building a new language runtime, extending Emacs, or designing an AI‑driven SaaS product, consider adopting a tagged‑pointer + Poor Man’s Inheritance strategy. It offers a proven path to low latency, predictable memory usage, and easy extensibility.