Trampolining Nix with GenericClosure

Nix jumps the 10k crash wall with a trampoline hack—and the comments went wild

TLDR: A clever Nix trick repurposes genericClosure to dodge stack overflows without real loops. Readers split between applauding the hack and demanding proper tail-call optimization, while a proposed trampoline builtin languishes with little community traction; one commenter pointed to a nerdier deep-dive instead.

Nix, the famously brainy package manager with its own programming language, keeps face-planting after about 10,000 “steps” thanks to no loops and deep recursion. Enter a cheeky workaround: use genericClosure (a dependency tool!) as a DIY trampoline to keep the stack shallow and the app alive. It’s clever, it’s fast, and it sidesteps the dreaded crash. Meanwhile, the official evaluator still doesn’t do tail-call optimization (a way to reuse memory in long chains), and a proposed builtins.trampoline has… three upvotes. The vibe? Crickets.

The community reaction split hard. One camp cheers, “Hacky? Sure. But it works—ship it!” Another fires back: “Stop duct-taping Nix—fix the evaluator.” Folks called out real pain points like long configs exploding and basic tools like splitString tapping out. Memes flew about Nix’s “no loops” life, and jokes about a mythical builtins.while had everyone nodding. The Rust-based Tvix getting tail-call love sparked the usual Rust-vs-C++ mini-drama. And like a quiet bomb, user ret2pop dropped a link to a brain-melting sibling read: dependent types in pure Nix. Translation: if you wanted less drama and more math, the door’s over there. The mood? Amused, exasperated, and very, very Nix.

Key Points

  • Nix relies on recursion for iteration and, since Nix 2.20, enforces a default 10,000-call evaluator depth cap configurable via max-call-depth.
  • The reference C++ Nix evaluator lacks tail-call optimization due to its structure, while Tvix (Rust-based) supports TCO in many cases.
  • Real-world overflows can occur (e.g., lib.splitString on ~280-line inputs and deep NixOS configurations with home-manager and stylix).
  • builtins.genericClosure implements a non-recursive worklist algorithm in C++, enabling O(1) stack depth and improving performance (15.5x speedup replacing lib.closePropagation in nixpkgs).
  • Repurposing genericClosure as a trampoline lets computations iterate step-by-step via nodes, terminating with [], achieving O(1) stack usage; Nix lacks while/until and a proposed builtins.trampoline is unmerged.

Hottest takes

"A sibling post: https://blog.kleisli.io/post/dependent-types-in-pure-nix" — ret2pop
Made with <3 by @siedrix and @shesho from CDMX. Powered by Forge&Hive.