Nix を使ってラフに Haskell を書ける環境を手に入れる

2024-12-14

はじめに

カジュアルに Haskell スクリプトを書く方法をいろいろ模索していたのですが、Nix を使う方法に落ち着きました。

完成形は https://github.com/logicoffee/nix-haskript に置いてあります。

Nix とは

Nix は自分のことを以下のように言っています。

Nix, the purely functional package manager

分離可能かつ共有可能な環境を構成することができるという意味では Docker のような側面を持っています。

また、ホストマシンのグローバルな環境を構成することができるという意味ではパッケージマネージャーの側面も持っています。

準備

Nix のインストール

Nix のインストールは Zero to Nix の方法でやるのをおすすめします。

書いてあるとおりにコマンドを実行すれば完了します。

direnv のインストール

direnv は必須ではないですが、Nix と連携させるととても便利なので導入するのがおすすめです。

自分の場合、グローバルにインストールするパッケージは home-manager で管理しているのですが、home-manager の導入自体も面倒なので、以下のようなコマンドでさくっとインストールしてしまうのがいいでしょう。もちろん Homebrew などでインストール済みのものがあればそれを使うのでも構いません。

nix profile install direnv

使い方

以下を順に実行します。

git clone https://github.com/logicoffee/nix-haskript
cd nix-haskript
direnv allow

direnv がアクティベートされると、ghc などがインストールされます。それが完了すると、パス内に ghc が存在するため、以下のように Haskell スクリプトを実行することができます。

$ runghc main.hs
"foo"

これにて、ラフに Haskell スクリプトを実行する環境を手に入れることができました!

解説

ファイル構成

ファイル説明
flake.nix必要なライブラリやスクリプトのコンパイル方法などを記述
flake.locknixpkgs のコミットハッシュなどを保持
hie.yamlhaskell-language-server の設定ファイル
main.hs
Dockerfile

GHC バージョンと Haskell パッケージ

GHC バージョンは以下の部分で変更できます。必要な Haskell パッケージもここに列挙しましょう。Nix expression のリスト表記はコンマが不要である点に注意です。

また、追加可能な Haskell パッケージは ここ で検索できます。

ghc = (pkgs.haskell.packages.ghc96.ghcWithPackages (p: with p; [
  haskell-language-server
  text
]));

その他依存プログラム

開発時に必要なプログラムは packages に列挙します。

devShell = pkgs.mkShell {
  packages = [ ghc ];
};

例えば cowsay コマンドが欲しければ以下のように追記します。

devShell = pkgs.mkShell {
  packages = [ ghc pkgs.cowsay ];
};

コンパイル

ローカルで動かす分には runghc を使えばいいですが、例えばプログラムを Docker 化したい場合はインストール手順を書く必要があります。

コンパイル時に追加でプログラムが必要な場合は、上の devShell と同様に、buildInputs = [ ghc pkgs.cowsay ] のように書きます。

packages.default = pkgs.stdenv.mkDerivation {
  name = "";
  buildInputs = [ ghc ];
  src = ./.;
  buildPhase = ''
    ghc main.hs
  '';
  installPhase = ''
    mkdir -p $out/bin
    cp main $out/bin
  '';
};

nixpkgs のバージョン固定

nixpkgs のバージョンを固定したい場合は以下のように変更します。

- nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";

おわりに

いろいろ試行錯誤してやっとこさ Nix ファイルの書き方が分かってきました。でもこれで Haskell スクリプティングは手軽になりました。

一方で Haskell プロジェクトを構成するツールである stack や cabal を Nix と組み合わせる自分好みの方法についてはまだ見つけられていません。こっちは長旅になりそうです。