Partager cette page

Apprendre X en Y minutes

Où X=nix

Nix est un langage fonctionnel développé pour le gestionnaire de paquet Nix et NixOS.

Les expressions Nix peuvent être évaluées avec nix-instantiate ou nix repl.

with builtins; [

  #  Commentaires
  #=========================================

  # Ceci est un commentaire en ligne.

  /* Ceci est un commentaire
     écrit sur plusieurs lignes. */


  #  Booléens
  #=========================================

  (true && false)               # Et
  #=> false

  (true || false)               # Ou
  #=> true

  (if 3 < 4 then "a" else "b")  # Test logique
  #=> "a"


  #  Entiers et nombres flottants
  #=========================================

  # Il y a deux types de nombres : les entiers et les flottants.

  1 0 42 (-3)       # Quelques exemples d'entiers

  123.43 .27e13     # Quelques exemples de nombre flottants

  # Les opérations conservent le type du nombre

  (4 + 6 + 12 - 2)  # Addition
  #=> 20
  (4 - 2.5)
  #=> 1.5

  (7 / 2)           # Division
  #=> 3
  (7 / 2.0)
  #=> 3.5


  #  Chaînes de caractères
  #=========================================

  "Les chaînes de caractères littérales sont écrites entre guillements."

  "
    Les chaînes de caractères littérales
    peuvent s'étendre sur
    plusieurs lignes
  "

  ''
    Ceci est ce qu'on appelle une
    "chaîne de caractères littérale indentée".
    Les espaces de début de lignes sont intelligemment supprimées.
  ''

  ''
    a
      b
  ''
  #=> "a\n  b"

  ("ab" + "cd")   # Concaténation de chaînes de caractères
  #=> "abcd"

  # L'antiquotation vous permet d'intégrer des valeurs
  # dans des chaînes de caracères.
  ("Votre répertoire personnel est ${getEnv "HOME"}")
  #=> "Votre répertoire personnel est /home/alice"


  #  Chemins
  #=========================================

  # Nix a un type de variable primitif pour les chemins.
  /tmp/tutorials/learn.nix

  # Un chemin relatif est résolu en un chemin absolu
  # au moment de l'évaluation.
  tutorials/learn.nix
  #=> /the-base-path/tutorials/learn.nix

  # Un chemin doit toujours contenir au moins une barre oblique (slash).
  # Un chemin relatif d'un fichier dans le répertoire courant
  # a donc besoin du préfixe `./`
  ./learn.nix
  #=> /the-base-path/learn.nix

  # L'opérateur `/` doit être entouré d'espaces afin d'être
  # traité comme une division
  7/2        # Ceci est un chemin
  (7 / 2)    # Ceci est une division entière


  #  Importations
  #=========================================

  # Un fichier nix contient une seule expression principale sans
  # variable libre.
  # Une expression importée s'évalue à la valeur du fichier importé.
  (import /tmp/foo.nix)

  # Les importations peuvent également être spécifiées par des chaînes
  # de caractères.
  (import "/tmp/foo.nix")

  # Les chemins doivent être absolus. Cependant comme les chemins
  # relatifs sont automatiquement résolus, ils peuvent être utilisés
  # pour l'importation.
  (import ./foo.nix)

  # Attention, cela ne se produit pas avec les chaînes de caractères.
  (import "./foo.nix")
  #=> error: string ‘foo.nix’ doesn't represent an absolute path


  #  Let
  #=========================================

  # Les blocs `let` permettent d'affecter des valeurs avec des variables.
  (let x = "a"; in
    x + x + x)
  #=> "aaa"

  # Les affectations peuvent se référer les unes aux autres, et leur ordre
  # n'a pas d'importance.
  (let y = x + "b";
       x = "a"; in
    y + "c")
  #=> "abc"


  # Les affectations d'un bloc fille écrasent les affections du bloc mère.
  (let a = 1; in
    let a = 2; in
      a)
  #=> 2


  #  Fonctions
  #=========================================

  (n: n + 1)      # Fonction qui ajoute 1

  ((n: n + 1) 5)  # Fonction précédente appliquée à 5
  #=> 6

  # Il n'y a pas de syntaxe pour nommer les fonctions,
  # mais elles peuvent affectée à une variable par un bloc `let`
  # comme toutes les autres valeurs.
  (let succ = (n: n + 1); in succ 5)
  #=> 6

  # Une fonction a exactement un seul argument.
  # Des fonctions à plusieurs arguments peuvent être construites
  # par imbrificication de fonctions.
  ((x: y: x + "-" + y) "a" "b")
  #=> "a-b"

  # Il est également possible d'avoir des arguments de fonction nommés.
  # Nous verrons comment après avoir introduit les ensembles.


  #  Listes
  #=========================================

  # Les listes sont indiquées par des crochets.

  (length [1 2 3 "x"])
  #=> 4

  ([1 2 3] ++ [4 5])
  #=> [1 2 3 4 5]

  (concatLists [[1 2] [3 4] [5]])
  #=> [1 2 3 4 5]

  (head [1 2 3])
  #=> 1
  (tail [1 2 3])
  #=> [2 3]

  (elemAt ["a" "b" "c" "d"] 2)
  #=> "c"

  (elem 2 [1 2 3])
  #=> true
  (elem 5 [1 2 3])
  #=> false

  (filter (n: n < 3) [1 2 3 4])
  #=> [ 1 2 ]


  #  Ensembles
  #=========================================

  # Un ensemble, ou "set", est un dictionnaire non ordonné avec des clés
  # en chaîne de caractères.
  { foo = [1 2]; bar = "x"; }

  # L'opérateur `.` extrait une valeur d'un ensemble.
  { a = 1; b = 2; }.a
  #=> 1

  # L'opérateur `?` teste si la clé est présente dans l'ensemble.
  ({ a = 1; b = 2; } ? a)
  #=> true
  ({ a = 1; b = 2; } ? c)
  #=> false

  # L'opérateur `//` fusionne deux ensembles.
  ({ a = 1; } // { b = 2; })
  #=> { a = 1; b = 2; }

  # Les valeurs de droite écrasent les valeurs de gauche.
  ({ a = 1; b = 2; } // { a = 3; c = 4; })
  #=> { a = 3; b = 2; c = 4; }

  # Le mot clé `rec` indique un ensemble récursif, ou "recursive set",
  # dans lequel les attributs peuvent se référer les uns aux autres.
  (let a = 1; in     { a = 2; b = a; }.b)
  #=> 1
  (let a = 1; in rec { a = 2; b = a; }.b)
  #=> 2

  # Les ensembles imbriqués peuvent être définis par morceaux.
  {
    a.b   = 1;
    a.c.d = 2;
    a.c.e = 3;
  }.a.c
  #=> { d = 2; e = 3; }

  # Les ensembles sont immuables, il est donc impossible de rédéfinir
  # un attribut :
  {
    a = { b = 1; };
    a.b = 2;
  }
  #=> attribute 'a.b' at (string):3:5 already defined at (string):2:11

  # Cependant un attribut d'un attribut de l'ensemble peut également
  # être défini par morceaux même si l'attribut père a été directement
  # défini.
  {
    a = { b = 1; };
    a.c = 2;
  }
  #=> { a = { b = 1; c = 2; }; }


  #  With
  #=========================================

  # Le corps d'un bloc `with` est évalué avec
  # les mappings d'un ensemble liés à des variables.
  (with { a = 1; b = 2; };
    a + b)
  # => 3

  # Les affectations d'un bloc fille écrasent les affections du bloc mère.
  (with { a = 1; b = 2; };
    (with { a = 5; };
      a + b))
  #=> 7

  # La première ligne du tutoriel commence par `with builtins;`
  # car `builtins` est un ensmble qui contient toutes les fonctions
  # de base (`length`, `head`, `tail`, `filter`, etc.). Cela permet
  # de ne pas avoir à écrire `builtins.length` au lieu de simplement
  # `length` par exemple.


  #  Modèles d'ensemble
  #=========================================

  # Les ensembles sont utiles pour passer plusieurs valeurs
  # à une fonction.
  (args: args.x + "-" + args.y) { x = "a"; y = "b"; }
  #=> "a-b"

  # On peut l'écrire plus clairement en utilisant des modèles d'ensemble,
  # ou "set patterns".
  ({x, y}: x + "-" + y) { x = "a"; y = "b"; }
  #=> "a-b"

  # Par défaut, le modèle échoue si l'ensemble contient des clés
  # supplémentaires.
  ({x, y}: x + "-" + y) { x = "a"; y = "b"; z = "c"; }
  #=> error: anonymous function called with unexpected argument ‘z’

  # L'ajout de `, ...` permet d'ignorer les clés supplémentaires.
  ({x, y, ...}: x + "-" + y) { x = "a"; y = "b"; z = "c"; }
  #=> "a-b"


  #  Erreurs
  #=========================================

  # `throw` provoque l'abandon de l'évaluation avec un message d'erreur.
  (2 + (throw "foo"))
  #=> error: foo

  # `tryEval` permet de capturer les erreurs.
  (tryEval 42)
  #=> { success = true; value = 42; }
  (tryEval (2 + (throw "foo")))
  #=> { success = false; value = false; }

  # `abort` est comme `throw`, mais l'erreur est alors fatale :
  # elle ne peut pas être capturée.
  (tryEval (abort "foo"))
  #=> error: evaluation aborted with the following error message: ‘foo’

  # `assert` s'évalue à la valeur donnée si le test est vrai;
  # sinon il lève une exception capturable.
  (assert 1 < 2; 42)
  #=> 42
  (assert 1 > 2; 42)
  #=> error: assertion failed at (string):1:1
  (tryEval (assert 1 > 2; 42))
  #=> { success = false; value = false; }


  #  Impureté
  #=========================================


  # La répétabilité des constructions étant critique pour le
  # gestionnaire de paquets Nix, la pureté fonctionnelle est
  # mise en avant dans le langage Nix. Cependant, il existe des
  # impuretés.

  # Vous pouvez vous référer aux variables d'environnement.
  (getEnv "HOME")
  #=> "/home/alice"


  # La fonction `trace` est utilisée pour le débogage.
  # Elle affiche le premier argument dans `stderr` et
  # évalue le second argument.
  (trace 1 2)
  #=> trace: 1
  #=> 2

  # Vous pouvez écrire des fichiers dans le magasin Nix (Nix store).
  # Bien qu'impur, c'est assez sûr car le nom du fichier est dérivé
  # du hachage de  son contenu. On peut lire des fichiers depuis n'importe où.
  # Dans cet exemple, on écrit un fichier dans le magasin, puis on le relit.
  (let filename = toFile "foo.txt" "hello!"; in
    [filename (builtins.readFile filename)])
  #=> [ "/nix/store/ayh05aay2anx135prqp0cy34h891247x-foo.txt" "hello!" ]

  # Il est également possible de télécharger des fichiers dans le magasin Nix.
  (fetchurl "https://example.com/package-1.2.3.tgz")
  #=> "/nix/store/2drvlh8r57f19s9il42zg89rdr33m2rm-package-1.2.3.tgz"

]

Pour en savoir plus (anglais)


Vous avez une suggestion ? Peut-être une correction ? Ouvrez un ticket sur GitHub, ou faites vous-même une pull request !

Version originale par Chris Martin, mis à jour par 2 contributeur(s).