URI:
       My Nix exploration
       2024-06-24
       Last edit: 2024-06-24
       ---------------------
       
       Here I share some notes and other things I've learned about Nix that I find interesting. The content of this post is mainly about me learning Nix, it's not about understanding the whole tool and language.
       
       Also, it's important to note that I use Nix as a non-NixOS user.
       
       ## What is Nix?
       
       Nix is actually several things! It's a cross platform package manager. It would be a little more accurate to say that it's a deployment tool used as a package manager.
       
       And it's also a purely functional programming language, dynamically typed and lazily evaluated.
       
       ## Learning the programming language
       
       I started by learning the basics of the language and then went on to explore it in a bit more depth.
       
       ### The basics
       
       I read 
  HTML Nix language basics](https://nix.dev/tutorials/nix-language#reading-nix-language) and to get used to the language I practised with [A tour of Nix
        which has several levels of difficulty from "easy" to "hard".
       
       One interesting thing about this language is that it has only one argument per function. To simulate several arguments, you can, for example, write a function with one argument that returns a function with one argument that returns a function with one argument, and so on. The syntax of the language makes it easy to do this.
       
       I was taught that it has a name, it's called 
  HTML Currying
       . It's the transformation of a function with several arguments into a function with one argument that returns a function on the rest of the arguments. Here's an example with arguments `3` and `4`.
       
       ```nix
       nix-repl> (a: b: a + b) 3 4
       7
       ```
       
       A Python equivalent might be something like the following.
       
       ```python
       >>> (lambda a: lambda b: a + b)(3)(4)
       7
       ```
       
       Another solution that is often used, particularly in 
  HTML Nixpkgs
       , is to have an attribute set as a parameter to the function, and to use the attributes as arguments. For example, this might look like the expression below.
       
       ```nix
       nix-repl> ({a, b}: a + b){a = 3; b = 4;}
       7
       ```
       
       ### Fake dynamic binding
       
       Although the blog post 
  HTML How to Fake Dynamic Binding in Nix
        talks about this very well, I find it interesting to offer my own thoughts and approach.
       
       The language is statically scoped, i.e. binding decisions are made according to the scope at declaration time.
       
       Let's look at the `rec` keyword, which allows an attribute set to access its own attributes (recursive binding). Here's an example.
       
       ```nix
       nix-repl> rec { a = 1; b = a + 1;}
       {
         a = 1;
         b = 2;
       }
       ```
       
       This is an interesting feature, but it remains static because the binding is done before the runtime. This poses problems, particularly when it comes to overriding attributes, as shown in the example below.
       
       ```nix
       nix-repl> rec { a = 1; b = a + 1; } // { a = 10; }
       {
         a = 10;
         b = 2;
       }
       ```
       
       In this example, we would like `b` to be equal to `11`, not `2`.
       
       To solve this problem, we can look at the concept of a fixed point. A fixed point is a value of `x` that validates the equation `x = f(x)`.
       
       We can therefore write the following function.
       
       ```nix
       nix-repl> fix = f: let
         result = f result;
       in
         result
       ```
       
       So here we have the function `fix` which takes a function `f` as a parameter and returns the fixed point `result` of the function `f`.
       
       You might be tempted to say that the `f` function calls itself ad infinitum (`f(f(f(f(..))))`), but Nix evaluates expressions lazily, so this isn't the case.
       
       We can literally see that the `f` function returns a fixed point (`result`), because `result = f result`, which respects the definition of a fixed point.
       
       The `fix` function will allow us to emulate the `rec` keyword, as shown in the example below.
       ```nix
       nix-repl> fix (self: { a = 3; b = 4; c = self.a + self.b; })
       {
         a = 3;
         b = 4;
         c = 7;
       }
       ```
       
       To better understand how it works, I've written the result of the `fix` function differently with the argument used previously.
       
       ```nix
       nix-repl> let
         result = { a = 3; b = 4; c = result.a + result.b;};
       in
         { a = 3; b = 4; c = result.a + result.b;}
       {
         a = 3;
         b = 4;
         c = 7;
       }
       ```
       
       Finally, I've written the following function, which will allow the attributes to be overridden dynamically as initially intended.
       
       ```nix
       nix-repl> fix = let
         fixWithOverride = f: overrides: let
             result = (f result) // overrides;
           in
             result // { override = x: fixWithOverride f x; };
       in
       f: fixWithOverride f {}
       
       attrFunction = self: { a = 3; b = 4; c = self.a+self.b; }
       
       attrFunctionFixedPoint = fix attrFunction
       
       nix-repl> attrFunctionFixedPoint
       {
         a = 3;
         b = 4;
         c = 7;
         override = «lambda override @ «string»:5:30»;
       }
       
       nix-repl> attrFunctionFixedPoint.override { b = 1; }
       {
         a = 3;
         b = 1;
         c = 4;
         override = «lambda override @ «string»:5:30»;
       }
       ```
       
       ## The essential Nix tool
       
       As already mentioned, the main use of Nix is cross platform package management. In this section I'm just trying to share and summarise some of the essential parts of my notes. If you want more details, I recommend you read the excellent 
  HTML Nix Pills
       . It's rather long but well worth the read!
       
       ### How does it work ?
       
       To sum up, I'd say that the Nix language has a very interesting native function called `derivation` (
  HTML see documentation
       ) on which many Nix expressions are based. I'm not going to redefine the term because the documentation has a very comprehensible version, but the important thing to remember is that a derivation is a construction specification, it's an immutable Nix building block. With another package manager, you could see it as a literal package.
       
       Nix technology will enable us to build these derivations, in the following stages.
       
       The `.drv` files contain specifications on how to build the derivation, they are intermediate files comparable to `.o` files, and the `.nix` files are comparable to `.c` files.
       
       The construction result is immutable and will be stored in `/nix/store/`, a synchronisation with the 
  HTML SQLite
        database. I said it was immutable, in fact it is because Nix creates a hash for the path in the `/nix/store/` from the input derivation (not from the construction result).
       
       It's pretty hard to imagine all this, so I'll give you a concrete example. Let's imagine I want to create a derivation for the famous software 
  HTML GNU Hello
       . The Nix derivation could look something like this.
       
       ```nix
       # default.nix
       
       let
         pkgs = import  { };
       in
         {
           hello = pkgs.stdenv.mkDerivation {
             pname = "hello";
             version = "2.12.1";
       
             src = fetchTarball {
               url = "https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz";
               sha256 = "1kJjhtlsAkpNB7f6tZEs+dbKd8z7KoNHyDHEJ0tmhnc=";
             };
           };
         }
       ```
       
       > The `mkDerivation` function is based on the `derivation` builtin function.
       
       It can be built with the following command.
       
       ```bash
       nix-build
       ```
       
       The build result has been created in `/nix/store/x9cc4jsylk5q01iaxmxf941b59chws5h-hello-2.12.1` and a symbolic link named `result` pointing to this folder has been created in the current folder. We can then find the binary in `./result/bin/hello`.
       
       Before the build, a `.drv` file was created, which can be found by running the following command.
       ```bash
       nix derivation show ./result | jq "keys[0]"
       ```
       
       The full path to the `.drv` file is found in the first key of the JSON object, so the path to the `.drv` file is `/nix/store/dp5z62k3chf019biikg77p2acmz17phx-hello-2.12.1.drv`.
       
       As it is in binary format we can use `nix derivation show` to display the construction information it contains with the following command.
       
       ```bash
       nix derivation show (nix derivation show ./result | jq "keys[0]" | tr -d "\"")
       # Or
       nix derivation show /nix/store/dp5z62k3chf019biikg77p2acmz17phx-hello-2.12.1.drv
       # ^
       # | Same output
       # v
       nix derivation show ./result
       ```
       
       ### Nixpkgs
       
       In the Nix expression used previously (the 
  HTML GNU Hello
        derivation), I used the `mkDerivation` function from `stdenv`.
       
       This function is not builtin, it comes from the `pkgs` identifier which has the value `import  { };`.
       
       Before explaining this import, I think it's very important to understand what 
  HTML Nixpkgs
        is. It's a Git repository that contains all the Nix expressions and modules. When this folder is evaluated, it produces an attribute set containing `stdenv`, which is itself an attribute set containing our `mkDerivation` function.
       
       Getting back to `pkgs`, `` is just a special Nix syntax, which, when evaluated, gives a path to a folder containing a collection of Nix expressions, i.e. Nixpkgs.
       
       Incidentally `` has an equivalence in Nix as shown below.
       
       ```nix
       nix-repl> 
       /home/nagi/.nix-defexpr/channels/nixpkgs
       
       nix-repl> builtins.findFile builtins.nixPath "nixpkgs"
       /home/nagi/.nix-defexpr/channels/nixpkg
       
       nix-repl> :p builtins.nixPath
       [
         {
           path = "/home/nagi/.nix-defexpr/channels";
           prefix = "";
         }
       ]
       ```
       
       ### Managing multiple Python versions
       
       One of the advantages of Nix is that it naturally offers the possibility of managing several versions of the same application. Taking 
  HTML Python
        as an example, let's say I want a Nix shell with version 3.7 and version 3.13.
       
       To do this, we can check for which version of 
  HTML Nixpkgs
        Python was built on version 3.7 and target a specific version of 
  HTML Nixpkgs
        in our Nix expression.
       
       To do this, there's the 
  HTML flox](https://floxdev.com/) tool which works very well, but to make it easier to understand I prefer to use [nixhub.io
       .
       
       So I'm looking for a version of the Nix packages that corresponds to Python version 3.7, and I find `nixpkgs/aca0bbe791c220f8360bd0dd8e9dce161253b341#python37`.
       
       ```nix
       # shell.nix
       
       let
         pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-23.11") { };
         nixpkgs-python = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/aca0bbe791c220f8360bd0dd8e9dce161253b341.tar.gz") { };
       in
         pkgs.mkShell {
           buildInputs = [
             nixpkgs-python.python37
             pkgs.python313
           ];
         }
       ```
       
       You can build Python derivations and enter a Nix shell with the following command.
       
       ```bash
       nix-shell
       ```
       
       And we see that we have access to the two versions requested with the commands `python3.7` and `python3.13` !
       
       ## A Virtual environment in Python with Nix flakes
       
       I've recently created a development environment with Nix flakes (
  HTML see documentation
       ), it's very handy as it provides a ready to use environment for Python 3.11 with the desired modules.
       
       Below is a Nix expression I wrote for the Python module 
  HTML callviz
       , it has all the necessary dependencies and a virtual Python environment.
       
       ```nix
       # flake.nix
       
       {
         inputs = {
           nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
         };
       
         outputs =
           { self, nixpkgs }:
           let
             supportedSystems = [
               "x86_64-linux"
               "aarch64-linux"
               "x86_64-darwin"
               "aarch64-darwin"
             ];
       
             forEachSupportedSystem =
               f: nixpkgs.lib.genAttrs supportedSystems (system: f { pkgs = import nixpkgs { inherit system; }; });
           in
           {
             # ...
             # I usually also declare a default package, a code checker and formatter
             devShells = forEachSupportedSystem (
               { pkgs }:
               {
                 default = pkgs.mkShell {
                   venvDir = ".venv";
                   packages =
                     with pkgs;
                     [
                       python3
                       graphviz
                     ]
                     ++ (with pkgs.python3Packages; [
                       pip
                       venvShellHook
                       graphviz
                     ]);
                 };
               }
             );
           };
       }
       ```
       
       Note that the default package and the default development shell are compatible with all systems (`supportedSystems`)!
       
       To realise the derivations and enter the Nix shell, I can run the following command.
       ```bash
       nix develop
       ```
       
       ## Nixpkgs contribution
       
       Once I'd finished exploring and learning Nix, I wanted to make a package for [Super Mario War](http://smwstuff.net/game) and add it to 
  HTML Nixpkgs
       .
       
       Here's what the package looks like.
       
       ```nix
       {
         lib,
         stdenv,
         fetchFromGitHub,
         cmake,
         pkg-config,
         enet,
         yaml-cpp,
         SDL2,
         SDL2_image,
         SDL2_mixer,
         zlib,
         unstableGitUpdater,
         makeWrapper,
       }:
       stdenv.mkDerivation (finalAttrs: {
         pname = "supermariowar";
         version = "2023-unstable-2024-09-17";
       
         src = fetchFromGitHub {
           owner = "mmatyas";
           repo = "supermariowar";
           rev = "6b8ff8c669ca31a116754d23b6ff65e42ac50733";
           hash = "sha256-P0jV7G81thj0UJoYLd5+H5SjjaVu4goJxc9IkbzxJgs=";
           fetchSubmodules = true;
         };
       
         nativeBuildInputs = [
           cmake
           pkg-config
           makeWrapper
         ];
       
         buildInputs = [
           enet
           yaml-cpp
           SDL2
           SDL2_image
           SDL2_mixer
           zlib
         ];
       
         cmakeFlags = [ "-DBUILD_STATIC_LIBS=OFF" ];
       
         postInstall = ''
           mkdir -p $out/bin
       
           for app in smw smw-leveledit smw-worldedit; do
             makeWrapper $out/games/$app $out/bin/$app \
               --add-flags "--datadir $out/share/games/smw"
           done
       
           ln -s $out/games/smw-server $out/bin/smw-server
         '';
       
         passthru.updateScript = unstableGitUpdater { };
       
         meta = {
           description = "A fan-made multiplayer Super Mario Bros. style deathmatch game";
           homepage = "https://github.com/mmatyas/supermariowar";
           changelog = "https://github.com/mmatyas/supermariowar/blob/${finalAttrs.src.rev}/CHANGELOG";
           license = lib.licenses.gpl2Plus;
           maintainers = with lib.maintainers; [ theobori ];
           mainProgram = "smw";
           platforms = lib.platforms.linux;
         };
       })
       ```