DEV Community

Cover image for Nix for Fun and Profit: Programs as Lego's
Brandon Lucas
Brandon Lucas

Posted on

Nix for Fun and Profit: Programs as Lego's

Programs as Lego's

Nix was made to solve the software deployment problem, concisely defined by creator Eelco Dolstra thus:

[The software deployment problem] is about getting computer programs from one machine to another—and having
them still work when they get there.

The Purely Functional Software Deployment Model, Eelco Dolstra

Nix allows you to setup software on your computer in such a way that your setup is reproducible, meaning your setup on machine A can be exactly the same as your setup on machine B -- as long as you have Nix.

To most people, learning Nix is a pain, due to the new concepts, sparse and outdated documentation, and community .

But I think Nix can make using computers fun and powerful and less painful, once you learn how to handle its' edges.

One fun way we can use Nix is to stitch together programs like Lego's and have them interact with each other in a reproducible way. Let's create an example!

First install Nix if you haven't.

Say we have a fun little bash script like this:

#!/usr/bin/env bash

# filename: pokefortune.sh

message="${1:-}"
pokemon="${2:-slowking}"

# Silence Perl locale warnings.
export LC_ALL=C

# Generate a fortune if user did not pass a message.
if [[ -z "$message" ]]; then
  message=$(fortune)
fi

echo "$message" | pokemonsay -p "$pokemon" -n
Enter fullscreen mode Exit fullscreen mode

It optionally takes in a message and a Pokédex number, and prints out that Pokémon and message using the pokemonsay program. If either the message or the Pokemon aren't specified, it uses a default Pokemon and the program fortune to generate the message.

Save this in a file pokefortune.sh, make it executable, then run it:

# Make the bash script executable
chmod +x pokefortune.sh

./pokefortune.sh
Enter fullscreen mode Exit fullscreen mode

If you're like most people, you probably don't have pokemonsay or fortune installed on your system, so you'll likely see something like this:

./pokefortune.sh: line 6: fortune: command not found
./pokefortune.sh: line 7: pokemonsay: command not found
Enter fullscreen mode Exit fullscreen mode

Therefore this script, which works on my system because I have these programs installed, isn't reproducible on your system. Let's create a Nix derivation to make it so. Create a file called pokefortune.nix and copy the following:

# filename: pokefortune.nix

{
  pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") { },
}:
pkgs.writeShellScriptBin "pokefortune" ''

message="''${1:-}"
pokemon="''${2:-slowking}"

# Silence Perl locale warnings.
export LC_ALL=C

# Generate a fortune if user did not pass a message.
if [[ -z "$message" ]]; then
  message=''$(${pkgs.fortune}/bin/fortune)
fi

echo $message | ${pkgs.pokemonsay}/bin/pokemonsay -p "$pokemon" -n
''
Enter fullscreen mode Exit fullscreen mode

Now run:

nix-build pokefortune.nix
Enter fullscreen mode Exit fullscreen mode

This may take awhile, especially if this is your first time running Nix. Let's look at the output:

nix-build src/scripts/nix/pokefortune.nix
unpacking 'https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz' into the Git cache...
this derivation will be built:
  /nix/store/vg4zmghqcnhjbs8kqhx04xixvm36d3ik-pokefortune.drv
these 9 paths will be fetched (2.65 MiB download, 26.91 MiB unpacked):
  /nix/store/0vdf5mpd762bw53rgl5nkmhvzq8n4m0d-file-5.45
  /nix/store/77cdqhqprqbciyhzsnzmsk7azbk2xv6r-fortune-mod-3.20.0
  /nix/store/d0i8idmbb4jji9ml01xsqgykrbvm7dss-gnu-config-2024-01-01
  /nix/store/0554jm1l1qw1pcfqsliw91hnifn11w8m-gnumake-4.4.1
  /nix/store/2wp235bg03gykpixd9v2nyxp08w8xq8a-patchelf-0.15.0
  /nix/store/c5x32idp600dklz9n25q38lk78j5vwxb-pokemonsay-1.0.0
  /nix/store/pba53n11na87fs4c20mp8yg4j7qx1by2-recode-3.7.14
  /nix/store/zix67r268ihi4c362zw7c0989z12jmy7-stdenv-linux
  /nix/store/2329271b42wh6b6yhl7jmjyi0cs4428b-update-autotools-gnu-config-scripts-hook
copying path '/nix/store/d0i8idmbb4jji9ml01xsqgykrbvm7dss-gnu-config-2024-01-01' from 'https://cache.nixos.org'...
copying path '/nix/store/c5x32idp600dklz9n25q38lk78j5vwxb-pokemonsay-1.0.0' from 'https://cache.nixos.org'...
copying path '/nix/store/0vdf5mpd762bw53rgl5nkmhvzq8n4m0d-file-5.45' from 'https://cache.nixos.org'...
copying path '/nix/store/0554jm1l1qw1pcfqsliw91hnifn11w8m-gnumake-4.4.1' from 'https://cache.nixos.org'...
copying path '/nix/store/2wp235bg03gykpixd9v2nyxp08w8xq8a-patchelf-0.15.0' from 'https://cache.nixos.org'...
copying path '/nix/store/pba53n11na87fs4c20mp8yg4j7qx1by2-recode-3.7.14' from 'https://cache.nixos.org'...
copying path '/nix/store/2329271b42wh6b6yhl7jmjyi0cs4428b-update-autotools-gnu-config-scripts-hook' from 'https://cache.nixos.org'...
copying path '/nix/store/zix67r268ihi4c362zw7c0989z12jmy7-stdenv-linux' from 'https://cache.nixos.org'...
copying path '/nix/store/77cdqhqprqbciyhzsnzmsk7azbk2xv6r-fortune-mod-3.20.0' from 'https://cache.nixos.org'...
building '/nix/store/vg4zmghqcnhjbs8kqhx04xixvm36d3ik-pokefortune.drv'...
/nix/store/vsv2spw517cwq791fl3f8iymm6hshhyq-pokefortune
Enter fullscreen mode Exit fullscreen mode

Nix fetched the packages we specified in our .nix file: pokemonsay, fortune, and some we didn't specify: such as file, patchelf, and recode, which one or both of the other two packages depends on. Then it copied them locally, built a binary, and placed it at /nix/store/vsv2spw517cwq791fl3f8iymm6hshhyq-pokefortune/bin. We can confirm this by running it:

/nix/store/vsv2spw517cwq791fl3f8iymm6hshhyq-pokefortune/bin
Enter fullscreen mode Exit fullscreen mode

You should see something like this:

Pokefortune Output

To be reproducible, Nix ensures that it knows about every single dependency needed to create a package at all times. Because of that, you can do fun things like this:

# Quickly enter a shell with the dependencies to create and display images.
nix-shell -p graphviz chafa

# Query the derivation's dependency graph, create a .png from it and display it:
    nix-store --query --graph $(nix-build pokefortune.nix) | \
    dot -Tpng -o pokefortune-dependency-graph.png && chafa pokefortune-dependency-graph.png
Enter fullscreen mode Exit fullscreen mode

Resulting in a view of every single dependency (the "closure") that our pokefortune program requires:

Pokefortune Dependency Graph

How cool is that! We can see the whole dependency tree for anything packaged with Nix!

You have just done something very powerful with Nix: You've created a reproducible derivation that anyone who has the package manager installed on their system can use, which they couldn't before.

This is just a taste of what Nix can do, but I hope that the potential is clear. As Farid Zakaria says in his blog post "Learn Nix the Fun Way":

Hopefully, seeing the fun things you can do with Nix might inspire you to push through the hard parts.

There is a golden pot 💰 at the end of this rainbow 🌈 awaiting you.

This article is directly inspired by Farid Zakaria's blog post Learn Nix the Fun Way. Check out his excellent blog here.

Special thanks to Russell Weas and veracius for their input to this article and code


References:

Top comments (0)