Le langage Nix est un langage fonctionnel pur conçu pour décrire des
paquets et des configurations de manière reproductible. Tout y est
expression : un nombre, une fonction, un fichier de configuration — chaque
élément retourne une valeur, rien ne produit d’effet de bord. Ce guide vous
fait découvrir chaque concept fondamental en le testant immédiatement dans le
REPL ou avec nix eval.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Manipuler les 7 types primitifs de Nix (entiers, flottants, chaînes, booléens, null, chemins, fonctions)
- Structurer des données avec les attribute sets (attrsets) et les listes
- Définir et appeler des fonctions (simples, curryfiées, à arguments nommés)
- Utiliser
let...in,with,inheritetif-then-else - Transformer des données avec les builtins (
map,filter,attrNames…) - Écrire des expressions complètes prêtes pour
shell.nixouflake.nix
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Le langage Nix est le socle de tout l’écosystème : chaque paquet est décrit par
une expression Nix, chaque shell.nix, chaque flake.nix, chaque module
NixOS est du code Nix. Comprendre le langage vous permet de :
- lire et modifier les fichiers
shell.nixouflake.nixde vos projets, - écrire vos propres dérivations pour empaqueter des logiciels,
- déboguer un environnement de développement qui ne se comporte pas comme prévu,
- comprendre les exemples de la documentation nixpkgs et NixOS,
- personnaliser une configuration NixOS module par module.
Le REPL : votre terrain d’expérimentation
Section intitulée « Le REPL : votre terrain d’expérimentation »Le REPL (Read-Eval-Print Loop) est un terminal interactif pour tester des expressions Nix. Lancez-le avec :
nix repl --expr '{}'Tapez une expression, Nix l’évalue et affiche le résultat. Tapez :q pour
quitter.
Vous pouvez aussi évaluer des expressions ponctuelles directement dans le terminal avec :
nix eval --expr '1 + 2'3Tous les exemples de ce guide sont testables avec l’une ou l’autre méthode.
Types primitifs
Section intitulée « Types primitifs »Nix possède 7 types de base. Vous pouvez vérifier le type de n’importe quelle
valeur avec builtins.typeOf :
Entiers et flottants
Section intitulée « Entiers et flottants »nix eval --expr '1 + 2'3nix eval --expr '7 / 2'3La division entre entiers est entière (pas d’arrondi, troncature). Pour obtenir un résultat décimal :
nix eval --expr '7.0 / 2.0'3.5nix eval --expr 'builtins.typeOf 42'"int"Chaînes de caractères
Section intitulée « Chaînes de caractères »Les chaînes sont délimitées par des guillemets doubles :
nix eval --expr '"hello" + " world"'"hello world"L’opérateur + concatène les chaînes (et additionne les nombres — Nix
distingue par le type des opérandes).
Interpolation avec ${} :
nix eval --expr 'let name = "Nix"; in "Hello ${name}"'"Hello Nix"Pour interpoler un nombre, convertissez avec toString :
let port = 8080; in "Port: ${toString port}"# "Port: 8080"Chaînes multilignes — délimitées par '' (deux apostrophes simples) :
'' Première ligne Deuxième ligne''L’indentation minimale commune est automatiquement supprimée.
Booléens
Section intitulée « Booléens »nix eval --expr 'true && false'falsenix eval --expr 'true || false'trueOpérateurs de comparaison : ==, !=, <, >, <=, >=.
nix eval --expr '3 > 2'trueNull et chemins
Section intitulée « Null et chemins »null # type "null" — absence de valeur/tmp # type "path" — chemin absolu./. # type "path" — chemin relatifRécapitulatif des types
Section intitulée « Récapitulatif des types »| Type | Exemples | builtins.typeOf |
|---|---|---|
| Entier | 42, -1, 0 | "int" |
| Flottant | 3.14, 7.0 | "float" |
| Chaîne | "hello", ''multi'' | "string" |
| Booléen | true, false | "bool" |
| Null | null | "null" |
| Chemin | /tmp, ./file.nix | "path" |
| Fonction | x: x + 1 | "lambda" |
Les listes sont des collections ordonnées, délimitées par des crochets et séparées par des espaces (pas de virgules) :
[ 1 2 3 ][ "hello" true 42 ] # types mixtes autorisésOpérations sur les listes
Section intitulée « Opérations sur les listes »nix eval --expr 'builtins.length [ 1 2 3 ]'3nix eval --expr 'builtins.head [ 10 20 30 ]'10nix eval --expr 'builtins.tail [ 10 20 30 ]'[ 20 30 ]nix eval --expr '[ 1 2 ] ++ [ 3 4 ]'[ 1 2 3 4 ]L’opérateur ++ concatène deux listes. builtins.elem teste l’appartenance :
nix eval --expr 'builtins.elem 3 [ 1 2 3 ]'trueAttribute sets (attrsets)
Section intitulée « Attribute sets (attrsets) »L’attribute set est la structure de données fondamentale de Nix — un
dictionnaire clé-valeur. Chaque attribut se termine par un ; :
{ name = "Alice"; age = 30; active = true;}Accès et manipulation
Section intitulée « Accès et manipulation »nix eval --expr '{ a = 1; b = 2; }.a'1Fusion avec //
Section intitulée « Fusion avec // »L’opérateur // fusionne deux attrsets. Les clés du côté droit écrasent celles
du côté gauche :
nix eval --expr '{ a = 1; b = 2; } // { b = 99; c = 3; }'{ a = 1; b = 99; c = 3; }Introspection
Section intitulée « Introspection »nix eval --expr 'builtins.attrNames { z = 1; a = 2; m = 3; }'[ "a" "m" "z" ]Les noms sont retournés triés alphabétiquement.
nix eval --expr 'builtins.attrValues { a = 10; b = 20; }'[ 10 20 ]Test d’existence avec ?
Section intitulée « Test d’existence avec ? »nix eval --expr '{ a = 1; } ? a'truenix eval --expr '{ a = 1; } ? b'falseCombiné avec or pour une valeur par défaut :
let config = { port = 8080; };in config.host or "localhost"# "localhost"Attrsets récursifs avec rec
Section intitulée « Attrsets récursifs avec rec »Un attrset normal ne peut pas référencer ses propres attributs. Avec rec, il
le peut :
rec { name = "hello"; fullName = "${name}-world"; # utilise 'name' du même attrset}# { name = "hello"; fullName = "hello-world"; }Fonctions
Section intitulée « Fonctions »En Nix, les fonctions sont des valeurs comme les autres. La syntaxe est
argument: corps — le : sépare l’argument du corps.
Fonctions simples
Section intitulée « Fonctions simples »nix eval --expr '(x: x * 2) 5'10La fonction x: x * 2 prend un argument x et retourne x * 2. On
l’appelle en lui passant 5 juste après.
Fonctions curryfiées (plusieurs arguments)
Section intitulée « Fonctions curryfiées (plusieurs arguments) »Nix ne supporte qu’un argument par fonction. Pour plusieurs arguments, on chaîne les fonctions :
nix eval --expr '(a: b: a + b) 3 4'7a: b: a + b est en fait a: (b: a + b) — une fonction qui retourne une
fonction.
Fonctions à arguments nommés (pattern matching)
Section intitulée « Fonctions à arguments nommés (pattern matching) »C’est la forme la plus courante dans les fichiers Nix. L’argument est un attrset destructuré :
nix eval --expr '({ name, age }: "${name} a ${toString age} ans") { name = "Alice"; age = 30; }'"Alice a 30 ans"Valeurs par défaut avec ?
Section intitulée « Valeurs par défaut avec ? »{ name, port ? 8080 }: "Server ${name} on port ${toString port}"Si port n’est pas fourni, la valeur 8080 est utilisée.
Arguments supplémentaires avec ...
Section intitulée « Arguments supplémentaires avec ... »{ name, port ? 8080, ... }: "Server ${name}"Le ... autorise l’appelant à passer des attributs supplémentaires sans
erreur.
Variables locales : let...in
Section intitulée « Variables locales : let...in »Le bloc let...in crée des variables locales immuables :
nix eval --expr 'let x = 5; y = 3; in x + y'8Les variables peuvent se référencer mutuellement :
let base = 10; double = base * 2; triple = base * 3;in double + triple# 50with — importer un scope
Section intitulée « with — importer un scope »Le mot-clé with importe tous les attributs d’un attrset dans le scope
courant :
nix eval --expr 'let s = { a = 1; b = 2; }; in with s; a + b'3Usage typique — éviter de répéter pkgs. :
# Sans withpackages = [ pkgs.git pkgs.vim pkgs.ripgrep ];
# Avec withpackages = with pkgs; [ git vim ripgrep ];inherit — raccourci d’assignation
Section intitulée « inherit — raccourci d’assignation »inherit copie des variables du scope englobant dans un attrset :
nix eval --expr 'let x = 1; y = 2; in { inherit x y; }'{ x = 1; y = 2; }inherit x y; est strictement équivalent à x = x; y = y;.
On peut aussi hériter depuis un autre attrset :
let config = { port = 8080; host = "localhost"; };in { inherit (config) port host; }# { port = 8080; host = "localhost"; }Conditionnels : if-then-else
Section intitulée « Conditionnels : if-then-else »En Nix, if est une expression qui retourne toujours une valeur :
nix eval --expr 'if 3 > 2 then "oui" else "non"'"oui"Le else est obligatoire — toute expression doit retourner une valeur.
Usage typique — paquets conditionnels :
let debugMode = true; debugTools = if debugMode then [ pkgs.htop pkgs.ncdu ] else [];in basePackages ++ debugToolsimport — charger un fichier Nix
Section intitulée « import — charger un fichier Nix »import charge et évalue un fichier .nix :
# Si config.nix contient { port = 8080; host = "localhost"; }let config = import ./config.nix;in config.port# 8080Si le fichier importé est une fonction, il faut l’appeler avec ses arguments :
# Si tools.nix contient { pkgs }: [ pkgs.git pkgs.vim ]let tools = import ./tools.nix { inherit pkgs; };in tools<nixpkgs> est un chemin spécial résolu via les channels ou le registre.
import <nixpkgs> {} retourne l’attrset gigantesque contenant tous les
paquets.
Builtins : fonctions intégrées
Section intitulée « Builtins : fonctions intégrées »Nix fournit des fonctions intégrées dans l’objet builtins. Voici les plus
utiles :
map — transformer une liste
Section intitulée « map — transformer une liste »nix eval --expr 'map (x: x * 2) [ 1 2 3 4 ]'[ 2 4 6 8 ]builtins.filter — filtrer une liste
Section intitulée « builtins.filter — filtrer une liste »nix eval --expr 'builtins.filter (x: x > 2) [ 1 2 3 4 5 ]'[ 3 4 5 ]Autres builtins utiles
Section intitulée « Autres builtins utiles »| Builtin | Usage | Exemple |
|---|---|---|
builtins.length | Taille d’une liste | builtins.length [ 1 2 3 ] → 3 |
builtins.head | Premier élément | builtins.head [ 10 20 ] → 10 |
builtins.tail | Liste sans le premier | builtins.tail [ 10 20 30 ] → [ 20 30 ] |
builtins.elem | Appartenance | builtins.elem 3 [ 1 2 3 ] → true |
builtins.attrNames | Clés d’un attrset | builtins.attrNames { a = 1; } → [ "a" ] |
builtins.attrValues | Valeurs d’un attrset | builtins.attrValues { a = 1; } → [ 1 ] |
builtins.hasAttr | Test d’attribut | builtins.hasAttr "a" { a = 1; } → true |
toString | Conversion en chaîne | toString 42 → "42" |
builtins.typeOf | Type d’une valeur | builtins.typeOf 42 → "int" |
Mettre tout ensemble : anatomie d’un shell.nix
Section intitulée « Mettre tout ensemble : anatomie d’un shell.nix »Voici comment les concepts se combinent dans un fichier shell.nix typique :
{ pkgs ? import <nixpkgs> {} }: # ← fonction à argument nommé + défaut
let # ← variables locales projectName = "mon-api";
pythonEnv = pkgs.python312.withPackages (ps: [ # ← lambda ps.requests ps.flask ]);
debugMode = true; debugTools = if debugMode # ← conditionnel then [ pkgs.htop pkgs.ncdu ] else [];inpkgs.mkShell { # ← attrset passé à une fonction name = "${projectName}-dev"; # ← interpolation
packages = with pkgs; [ # ← with pythonEnv git vim ] ++ debugTools; # ← concaténation de listes
PROJECT_NAME = projectName; # ← variable d'environnement}Chaque ligne utilise un concept que vous venez d’apprendre. Les guides suivants approfondissent les environnements de développement et la modularisation de ces fichiers.
Récapitulatif des concepts
Section intitulée « Récapitulatif des concepts »| Concept | Syntaxe | Description |
|---|---|---|
| Types | 42, "hello", true, null | 7 types primitifs, tout est expression |
| Listes | [ 1 2 3 ] | Collections ordonnées, séparées par des espaces |
| Attrsets | { clé = valeur; } | Dictionnaires, structure fondamentale de Nix |
| Fonctions | arg: corps | Valeurs de première classe, un seul argument |
| Args nommés | { a, b ? 0 }: ... | Destructuration d’attrset avec défauts optionnels |
| let … in | let x = 1; in x | Variables locales immuables |
| with | with pkgs; [ git ] | Importe les attributs dans le scope |
| inherit | { inherit x; } | Raccourci pour x = x |
| if-then-else | if c then a else b | Expression conditionnelle (else obligatoire) |
| Opérateur // | a // b | Fusion d’attrsets (droite écrase gauche) |
| Opérateur ++ | [ 1 ] ++ [ 2 ] | Concaténation de listes |
| import | import ./file.nix | Charge et évalue un fichier |
| Interpolation | "Hello ${name}" | Insertion d’expressions dans les chaînes |
À retenir
Section intitulée « À retenir »- Tout est expression en Nix : il n’y a pas d’instructions, chaque élément retourne une valeur.
- Les attrsets (
{ clé = valeur; }) sont la structure universelle — les paquets, les configurations, les modules sont tous des attrsets. - Les fonctions sont omniprésentes : chaque fichier
.nixest généralement une fonction qui attendpkgsou d’autres arguments. - Les variables sont immuables : une fois définies dans un
let, elles ne changent plus. with,inheritet//sont des raccourcis syntaxiques qui rendent le code plus lisible — ils ne sont pas obligatoires mais très courants.- Le REPL (
nix repl) etnix eval --exprsont vos meilleurs alliés pour expérimenter.