Nix is built on a trifecta of three things: functional language, derivations + nixpkgs and nixos.

Language Syntax

https://nix.dev/tutorials/nix-language.html
https://nix.dev/manual/nix/2.28/language/index.html

Use nix repl for REPL and nix-instantiate --eval for files

Attribute Set

In the form { … }, collection of name-value-pairs. Literally like json, except using semicolons instead of commas.

{
  string = "hello";
  integer = 1;
  float = 3.141;
  bool = true;
  null = null;
  list = [ 1 "two" false ];
  attribute-set = {
    a = "hello";
    b = 2;
    c = 2.718;
    d = false;
  }; # comments are supported
}

You can add rec { ... } to have it be able to recursively call it’s own attributes. Access using the . operator. Commas are not used.

with …; …

Think of it as “namespaces”. Allows to access attributes without repeating.

let
  a = {
    x = 1;
    y = 2;
    z = 3;
  };
in
with a; [ x y z ]

instead of
a.x, a.y, a.z

inherit

{ inherit x y; }

is the same as {x = x; y = y;}

you can also hinherit from a specific attribute set using ()

inherit (a) x y;

is the same as {x = a.x; y = a.y;}

let…in…

Used for assigning names to values, to execute in the in block

let
  b = a + 1;
  a = 1;
in
a + b

Strings

"" for strings. '' '' for multiline strings. ${} for interpolation

Functions

x: x + 1
{a, b}: a + b
 
{a, b ? 0}: a + b
{a, b, ...}: a + b
 
args@{a, b, ...}: a + b + args.c
{ a, b, ... }@args: a + b + args.c

In pure functional pattern, they only accept one parameter. To call, just put it after

f = {a, b}: a + b
f {a = 1; b = 2;}

To have multiple, you need to curry

Operators

https://nix.dev/manual/nix/2.28/language/operators.html

Cool ones:

  • ++ list concatenation
  • attrset ? attribute has
  • attrset // attrset “Update”, but its basically just a union, with duplicatings using second
  • arguments |> func piping

Builtins

https://nix.dev/manual/nix/2.28/language/builtins.html
Implemented in C++ for the language interpreter

Most of them you need to use builtins.<func>. Except for:

import

Parses a nix expression and returns

pkgs.lib

From nixpkgs. Not built inl, but useful funcs implemented in nix itself

Derivation

Derivations are a way to create reproducible builds, byte-for-byte. It’s stored in the format /nix/store/<hash>-file-name. Derivations can depend on each other, etc.

Derivations can be stored in cache.nixos.org, to download tarballs in FHS layout that can be unpacked. These allow maximal reproducibility. HGased based on merkle trees (binary trees based on hashes of children)

Fun fact: ultimate root of trust being some kind of stdenv tarball, seed-c, etc
So what can you do with Nix?

derivation {
	name: "hello"; # symbolic name
	system: "x86_64-linux" # system 
	builder: "/bin/bash" # command to build
	args: [];
	outputs: [ "out" ];  # default out. passed as environment variables
}

https://www.youtube.com/watch?v=5D3nUU1OVx8
https://nix.dev/manual/nix/2.28/language/derivations.html

Flakes

A way to use nix syntax to create reproducible “flakes” - file system trees

{
	description: "string describing flake";
	inputs: <>
	outputs: function 
	nixConfig: <> # attribute sets of values
	
}

https://nix.dev/manual/nix/2.28/command-ref/new-cli/nix3-flake.html#flake-references

{ self, ... }@inputs:
{
  # Executed by `nix flake check`
  checks."<system>"."<name>" = derivation;
  # Executed by `nix build .#<name>`
  packages."<system>"."<name>" = derivation;
  # Executed by `nix build .`
  packages."<system>".default = derivation;
  # Executed by `nix run .#<name>`
  apps."<system>"."<name>" = {
    type = "app";
    program = "<store-path>";
  };
  # Executed by `nix run . -- <args?>`
  apps."<system>".default = { type = "app"; program = "..."; };
 
  # Formatter (alejandra, nixfmt or nixpkgs-fmt)
  formatter."<system>" = derivation;
  # Used for nixpkgs packages, also accessible via `nix build .#<name>`
  legacyPackages."<system>"."<name>" = derivation;
  # Overlay, consumed by other flakes
  overlays."<name>" = final: prev: { };
  # Default overlay
  overlays.default = final: prev: { };
  # Nixos module, consumed by other flakes
  nixosModules."<name>" = { config, ... }: { options = {}; config = {}; };
  # Default module
  nixosModules.default = { config, ... }: { options = {}; config = {}; };
  # Used with `nixos-rebuild switch --flake .#<hostname>`
  # nixosConfigurations."<hostname>".config.system.build.toplevel must be a derivation
  nixosConfigurations."<hostname>" = {};
  # Used by `nix develop .#<name>`
  devShells."<system>"."<name>" = derivation;
  # Used by `nix develop`
  devShells."<system>".default = derivation;
  # Hydra build jobs
  hydraJobs."<attr>"."<system>" = derivation;
  # Used by `nix flake init -t <flake>#<name>`
  templates."<name>" = {
    path = "<store-path>";
    description = "template description goes here?";
  };
  # Used by `nix flake init -t <flake>`
  templates.default = { path = "<store-path>"; description = ""; };
}

Self in the flake refers tot he ENTIRE FLAKE

Flake-utils

Use nix-systems to have a listof all systems, and then flake-utils to make something for eachDefaultSystem.

Nixpkgs

Nixpkgs is just a flake, you want to use import nixpkgs {…} to get the actual pkgs. Use inherit system if you are using flake-utils

Overlays

https://nixos.wiki/wiki/Overlays

Allow packages to be changed / updated

        overlays = [ (import rust-overlay) ];

        pkgs = (import nixpkgs) {
          inherit system overlays;
        };

then call inherit overlays to use it

Tips

you should do someDep.inpuits.nixpkgs.follows="nixpkgs". tells nix to follow their own nixpkgs.