## title: Nix functions in pure Python
       ## date: "2026-04-30"
       
       ## Introduction
       
       I often come up with new ideas for computer projects when
       I’m bored and browsing the internet. One day, I had the idea
       to port the library component of nixpkgs to pure Python. I
       simply wanted to be able to use functions in nixpkgs from
       within Python. It’s a pretty old project, but I still think
       it’s cool, which is why I wanted to talk about it in this
       short blog post.
       
       ## My goal
       
       The main goal of this project is to keep myself busy. I’ve
       always thought the lib.fixedPoints.fix nixpkgs function was
       cool, so I wanted to at least be able to use that function
       in Python.
       
       Just to clarify, my goal isn’t to call NixCpp functions from
       Python, there is already the python-nix project for that. I
       want to rewrite the logic of Nix functions in pure Python,
       with no dependencies other than the Python interpreter.
 (HTM) NixCpp
 (HTM) python-nix
       
       ## Nixpkgs
       
       I’ve mentioned nixpkgs several times before, but what
       exactly is it? It’s a GitHub repository for NixOS that
       contains the official collection of Nix and NixOS
       expressions. These expressions include Nix packages, NixOS
       modules, functions, and more.
 (HTM) GitHub repository
       
       ### Library section
       
       The part of nixpkgs that I want to implement in Python is
       the library section; some functions cannot be implemented
       because they depend on NixCpp.
 (HTM) library section
 (HTM) NixCpp
       
       ## Laziness bypass
       
       The Nix interpreter has a somewhat special way of evaluating
       expressions; it uses what is known as lazy evaluation. This
       means expressions are only evaluated when the interpreter
       needs their value. The Python interpreter does not have this
       property, and certain functions in the lib section of
       nixpkgs require laziness, notably the lib.fixedPoints.fix
       nixpkgs function. That is why I had to find a solution to
       simulate a form of laziness.
 (HTM) Python interpreter
       
       ### Fix function
       
       This is a function in nixpkgs that allows you to find the
       fixed point of a function. This function relies entirely on
       the lazy property of the Nix interpreter. Below is its
       definition in Nix.
       
       fix =
         f:
         let
           x = f x;
         in
         x;
       
       Below are a few examples of how to call the function.
       
       nix-repl> lib.fix (self: {a = 1; b = self.a + 1})
       {
         a = 1;
         b = 2;
       }
       
       nix-repl> lib.fix (self: [1 ((lib.elemAt self 0) + 1)])
       [
         1
         2
       ]
       
       nix-repl> lib.fix (lib.fix (f: self: {a = 1; b = self.a +
       1;}))
       {
         a = 1;
         b = 2;
       }
       
       And here is my Python implementation.
       
       def fix(f: Callable) -> Any:
           """Compute the least fixed point of a function"""
       
           # Evaluates with the stub value
           result = f(StubSequence())
           # Final evaluation
           result = f(result)
       
           return result
       
       ### Little hack
       
       The hack involves evaluating the function f, passed as an
       argument, once by passing it a class that returns default
       values for its special Python methods. This way, we can
       retrieve the default values needed for the final evaluation.
       
       ### Example
       
       For example, let’s take the following Python function.
       
       g = lambda self: {"a": 1, "b": self["a"] + 1}
       
       You can imagine that this will produce something like this
       in the fix function.
       
       # fix(g)
       result = f(StubSequence())
       result = f({"a": 1, "b": 0 + 1})
       result = {"a": 1, "b": 2}
       
       It also works with lists and functions, as shown below.
       
       fix(lambda self: [1, 2, 3, self[0] + self[1])
       fix(fix(lambda x: lambda f: [1, 2, 3, f[0] + f[1]))
       
       ### Technical limitation
       
       Since this approach isn’t true laziness, it obviously has a
       limitation. It doesn’t allow for correctly resolving chains
       with a depth greater than one. That is, in a list or
       dictionary, a variable cannot depend on more than one other
       variable. I think it’s technically possible to work around
       this problem, for example by keeping track of dependency
       chains in a graph; if you’re interested, you can try
       implementing it in the project.
 (HTM) the project
       
       ## Function calling style
       
       Technically, all functions with more than one argument are
       curried, so they must be called as follows.
       
       find_single(lambda x: x == 3)("none")("multiple")([1, 9])
       
       But in this module, the functions, although curried, can be
       called in any way.
       
       # A more comfortable calling style, still returning a
       function that return a function
       first_part = find_single(lambda x: x == 3, "none")
       # More explicit calling style
       first_part("multiple")([1, 9])
       
       ## Progress
       
       I've implemented a number of the features I wanted—55.93% to
       be exact. Detailed progress is available in the project
       documentation. This progress report is generated using a
       script that I wrote in Python.
 (HTM) project documentation
 (HTM) a script
       
       ## Conclusion
       
       It’s a pretty fun and laid-back project that helps me pass
       the time sometimes. The only downside is certain differences
       between Python and Nix, such as Nix’s lazy evaluation, which
       can make porting to Python complicated.