Comment utiliser Nix pour développer en Haskell
Voir aussi : video youtube - code source
Nix est un gestionnaire de paquets et d’environnements logiciels qui est assez pratique pour développer en Haskell. Cet article en présente quelques fonctionnalités de base.
Exemple de base
On veut développer ou exécuter le code suivant :
import Data.Char (toUpper)
import Data.Function ((&))
import qualified Streamly.Internal.FileSystem.File as File
import qualified Streamly.Prelude as Stream
import qualified Streamly.Data.Unicode.Stream as Stream
import System.Environment (getArgs)
main :: IO ()
= do
main <- getArgs
args case args of
->
[filename]
Stream.unfold File.read filename& Stream.decodeUtf8
& Stream.map toUpper
& Stream.mapM_ putChar
-> putStrLn "usage: <filename>" _
Ce code dépend de la bibliothèque streamly
. Comme cette dernière n’est généralement pas installée de base, l’exécution de notre code échouera probablement :
$ runghc Main.hs /etc/hostname
Main.hs:3:1: error:
Could not find module ‘Streamly.Internal.FileSystem.File’
Use -v (or `:set -v` in ghci) to see a list of the files searched for.
|
3 | import qualified Streamly.Internal.FileSystem.File as File
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
Nix permet de définir des environnements logiciels pour résoudre ce problème de dépendances et à différents niveaux (système global, utilisateur, projet).
Script dans un nix-shell
Pour définir l’environnement de notre code d’exemple, on peut écrire le fichier shell.nix
suivant :
let
pkgs = import <nixpkgs> {};
ghc = pkgs.haskellPackages.ghcWithPackages (ps: with ps; [
streamly]);
in pkgs.stdenv.mkDerivation {
name = "haskell-env";
buildInputs = [ ghc ];
shellHook = "eval $(egrep ^export ${ghc}/bin/ghc)";
}
On peut ensuite charger cet environnement avec la commande nix-shell
, ce qui permettra d’avoir toutes dépendances nécessaires et donc d’exécuter notre code :
$ nix-shell
...
[nix-shell]$ runghc Main.hs /etc/hostname
NIXOS
[nix-shell]$ exit
$
Le fichier shell.nix
permet de faire beaucoup d’autres choses (voir la doc de haskell4nix), par exemple préciser la version du compilateur ou de la logithèque à utiliser, ajouter ou modifier des bibliothèques Haskell, inclure des dépendances générales (non spécifiques à Haskell), etc.
Script autonome
Pour éviter d’écrire le fichier shell.nix
et d’avoir à lancer un nix-shell
, on peut spécifier l’interpréteur et les options à utiliser directement dans le fichier de code :
#!/usr/bin/env nix-shell
#!nix-shell -i "runghc" -p "haskellPackages.ghcWithPackages (pkgs: [pkgs.streamly])"
import Data.Char (toUpper)
...
Ainsi, il suffit d’ajouter les droits d’exécution au fichier et on peut alors le lancer directement :
$ ./Main.hs /etc/hostname
NIXOS
Cependant, cette méthode est rarement une bonne idée car elle rend le code dépendant de Nix et n’est pas performante à l’exécution du fait de runghc
.
Projet cabal
Pour un vrai projet en Haskell, on utilise plutôt un outil de configuration de projet comme cabal. Pour cela, on écrit un fichier de configuration, comme ici upperize.cabal
, indiquant notamment les dépendances et les fichiers de code :
-version: 2.2
cabal: upperize
name: 1.0
version-type: Simple
build: MIT
license
common deps-options: -Wall -O
ghc-language: Haskell2010
default-depends: base, streamly
build
executable upperizeimport: deps
-source-dirs: app
hs-is: Main.hs main
Nix permet alors d’utiliser ce fichier upperize.cabal
automatiquement. Pour cela, il suffit d’écrire le fichier default.nix
suivant :
{ pkgs ? import <nixpkgs> {} }:
let drv = pkgs.haskellPackages.callCabal2nix "upperize" ./. {};
in if pkgs.lib.inNixShell then drv.env else drv
On peut ensuite ouvrir un nix-shell
et lancer les commandes cabal :
$ nix-shell
...
[nix-shell]$ cabal run upperize /etc/hostname
...
NIXOS
Nix peut également utiliser cette configuration pour réutiliser le code dans un autre projet, construire une image docker, etc. Par exemple, pour installer le programme sur le système :
$ nix-env -if .
installing 'upperize-1.0'
...
$ upperize /etc/hostname
NIXOS
Conclusion
Nix est assez pratique pour développer en Haskell. Pour des petits codes, il permet de créer simplement des environnements logiciels satisfaisants. Pour des gros projets, il permet de réutiliser automatiquement la configuration Haskell. Nix permet également de créer des envrionnements plus adaptés au développement, notamment avec des outils de documentation ou d’analyse de code. Enfin, Nix propose également un nouveau système de configuration intéressant : Flakes.