Nix evita errores de pila con nueva técnica

Fuentes: Trampolining Nix with genericClosure

Nix, un lenguaje de gestión de paquetes y construcción, a menudo se enfrenta a limitaciones de profundidad de pila debido a su naturaleza recursiva. Cuando se ejecutan operaciones iterativas (simuladas a través de recursión), el evaluador de Nix puede alcanzar un límite de 10.000 niveles de llamada, lo que provoca errores de desbordamiento de pila. Este límite puede ser un problema para configuraciones complejas de NixOS, bibliotecas o incluso para tareas sencillas como dividir una cadena larga. El artículo explora una técnica para eludir esta limitación utilizando builtins.genericClosure, una función integrada diseñada originalmente para calcular dependencias de paquetes.

genericClosure implementa un algoritmo de lista de trabajo que evita la recursión directa. En lugar de llamadas recursivas, utiliza un bucle C++ para iterar, lo que elimina el crecimiento de la pila. La clave de esta técnica es reutilizar genericClosure como un 'trampolín'. Esto implica transformar una función recursiva en una secuencia de 'nodos', donde cada nodo representa un paso de la computación. El operador de genericClosure toma un nodo, realiza un paso y devuelve el siguiente nodo. De esta manera, la computación se realiza en pequeños pasos dentro del bucle C++, evitando el desbordamiento de la pila.

Aunque inicialmente se consideró 'prácticamente inútil' debido a la sobrecarga de copiar el estado, el artículo revela un problema sutil conocido como la 'trampa del thunk'. Este problema ocurre cuando el estado dentro de la función 'trampolín' contiene elementos que no se fuerzan (evalúan) en cada paso. Esto crea una cadena de thunks (evaluaciones diferidas) que, al intentar obtener el resultado final, provoca un desbordamiento de pila en la pila de C++ subyacente. La solución es forzar la evaluación del estado en cada paso, utilizando builtins.deepSeq dentro de la expresión 'key' de cada nodo. Esto asegura que el estado se evalúe completamente en cada iteración, rompiendo la cadena de thunks y evitando el desbordamiento de pila.

Esta técnica, que no se ha documentado previamente, permite a Nix realizar cálculos complejos con una profundidad de pila teóricamente constante (O(1)). Es una solución ingeniosa para un problema común en Nix, aunque requiere una comprensión profunda de cómo funciona el evaluador y el sistema de evaluación perezosa (call-by-need) de Nix.