Nix

Nix è un linguaggio per espressioni che è puro, lazy e funzionale.

È puro nel senso che le operazioni compiute durante l’esecuzioni delle espressioni non hanno effetti secondari (nel senso ad esempio che non è possibile l’assegnazione di variabili ad un contesto diverso da quello locale).

È pigro (lazy) nel senso che solo le parti utilizzate dell’espressione vengono valutate, diversamente da molti altri linguaggi.

È funzionale perché le funzioni possono essere manipolate e trattate come altri tipi di valori.

Non deve essere considerato un linguaggio per l’utilizzo generale, ma bensì uno per la definizione di pacchetti software. Infatti, tra i suoi tipi di dati ve ne sono alcuni inconsueti, come:

  • il percorso (o path), che definisce una risorsa presente sul file-system;

  • la stringa con contesto: ogni stringa in Nix ha un contesto che viene utilizzato per tracciare le dipendenze: maggiori informazioni in questo
    post di Shea Levy.

Vedi anche il manuale.

Il nuovo sistema di condivisione chiamato flakes

È presente nella nuova versione 2.4, attualmente ancora in “preview”. Per approfondimenti vedi l’introduzione al nuovo sistema da parte di Eelco Dolstra. Oltre a quella risorsa c’è la pagina del wiki dedicata e il reference del comando nix flake nel nuovo manuale.

Puoi installare da zero Nix nella versione giusta utilizzando l’apposito installer. Devi comunque seguire le indicazioni descritte nel tutorial di Eelco per attivare le nuove parti sperimentali.

Tutto il sistema ha ancora carattere sperimentale anche se ormai è “fuori” da più di un anno e ormai è stata fatta anche la documentazione e tutti trovano molto improbabile che cambi in maniera sostanziale.

Come funziona la generazione di un pacchetto

Si compone di due passaggi principali:

  1. l’instanziazione è il processo che valuta una espressione Nix contenente una derivation e produce un file .drv;

  2. la realizzazione è il processo che a partire da un .drv costruisce il pacchetto (vedi nix-store --realise).

DOs e DON’Ts

Utilizzo di risorse da moduli importati

Per utilizzare risorse siano esse dati o funzioni presenti in moduli esterni è tipico trovare codice come il seguente:

# modulo foo.nix
#...
  let bar =  import ./bar.nix {};
  in with bar; {
    baz = zooFromBar;
  }

o

# modulo foo.nix
{pkgs, lib}:
  with lib;
  {
    foo = mkIf;
  }

Questo metodo di importazione è molto simile al from foo import * del Python e soffre del simile problema: a meno che le risorse utilizzate non siano molto comuni, quando il codice del modulo aumenta di complessità si tende ad avere perdere chiarezza rispetto alla sorgente di tali risorse. È quindi meglio utilizzare uno speciale import esplicito, specialmente per gli import più globali, quindi:

  let inherit (import ./bar.nix {}) zooFromBar;
  in {
    baz = zooFromBar;
  }

Come rendere possibile il raggiungimento di risorse esterne durante i build

Normalmente Nix non permette di accedere a risorse esterne via rete a causa del sandbox: per limitare questa restrizione si può configurare ~/.config/nix/nix.conf come segue:

sandbox = relaxed;

Poi nella derivation dove accedere all’esterno è necessario basterà aggiungere un attributo __noChroot = true;. Da usare con parsimonia in quanto può rendere il processo di creazione del pacchetto non puro.