Introduction
Introduction Statistics Contact Development Disclaimer Help
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 int…
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…
And it's also a purely functional programming language, dynamically typed and l…
## Learning the programming language
I started by learning the basics of the language and then went on to explore it…
### The basics
I read
Nix language basics](https://nix.dev/tutorials/nix-language#reading-nix-languag…
which has several levels of difficulty from "easy" to "hard".
One interesting thing about this language is that it has only one argument per …
I was taught that it has a name, it's called
Currying
. It's the transformation of a function with several arguments into a function …
```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
Nixpkgs
, is to have an attribute set as a parameter to the function, and to use the at…
```nix
nix-repl> ({a, b}: a + b){a = 3; b = 4;}
7
```
### Fake dynamic binding
Although the blog post
How to Fake Dynamic Binding in Nix
talks about this very well, I find it interesting to offer my own thoughts and…
The language is statically scoped, i.e. binding decisions are made according to…
Let's look at the `rec` keyword, which allows an attribute set to access its ow…
```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 do…
```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 poi…
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 an…
You might be tempted to say that the `f` function calls itself ad infinitum (`f…
We can literally see that the `f` function returns a fixed point (`result`), be…
The `fix` function will allow us to emulate the `rec` keyword, as shown in the …
```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` functio…
```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 t…
```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.…
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…
see documentation
) on which many Nix expressions are based. I'm not going to redefine the term b…
Nix technology will enable us to build these derivations, in the following stag…
The `.drv` files contain specifications on how to build the derivation, they ar…
The construction result is immutable and will be stored in `/nix/store/`, a syn…
SQLite
database. I said it was immutable, in fact it is because Nix creates a hash fo…
It's pretty hard to imagine all this, so I'll give you a concrete example. Let'…
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/x9cc4jsylk5q01iaxmxf941b59chws…
Before the build, a `.drv` file was created, which can be found by running the …
```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, …
As it is in binary format we can use `nix derivation show` to display the const…
```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
GNU Hello
derivation), I used the `mkDerivation` function from `stdenv`.
This function is not builtin, it comes from the `pkgs` identifier which has the…
Before explaining this import, I think it's very important to understand what
Nixpkgs
is. It's a Git repository that contains all the Nix expressions and modules. W…
Getting back to `pkgs`, `` is just a special Nix syntax, which, when evaluated,…
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 man…
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
Nixpkgs
Python was built on version 3.7 and target a specific version of
Nixpkgs
in our Nix expression.
To do this, there's the
flox](https://floxdev.com/) tool which works very well, but to make it easier t…
.
So I'm looking for a version of the Nix packages that corresponds to Python ver…
```nix
# shell.nix
let
pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-2…
nixpkgs-python = import (fetchTarball "https://github.com/NixOS/nixpkgs/archi…
in
pkgs.mkShell {
buildInputs = [
nixpkgs-python.python37
pkgs.python313
];
}
```
You can build Python derivations and enter a Nix shell with the following comma…
```bash
nix-shell
```
And we see that we have access to the two versions requested with the commands …
## A Virtual environment in Python with Nix flakes
I've recently created a development environment with Nix flakes (
see documentation
), it's very handy as it provides a ready to use environment for Python 3.11 wi…
Below is a Nix expression I wrote for the Python module
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 nix…
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 …
To realise the derivations and enter the Nix shell, I can run the following com…
```bash
nix develop
```
## Nixpkgs contribution
Once I'd finished exploring and learning Nix, I wanted to make a package for [S…
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 ga…
homepage = "https://github.com/mmatyas/supermariowar";
changelog = "https://github.com/mmatyas/supermariowar/blob/${finalAttrs.src…
license = lib.licenses.gpl2Plus;
maintainers = with lib.maintainers; [ theobori ];
mainProgram = "smw";
platforms = lib.platforms.linux;
};
})
```
You are viewing proxied material from tilde.pink. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.