Haskell Stack でライブラリを安定させる

(2017.1 新規作成.)

cabal-install コマンドではパッケージのバージョンの "組み合わせ" を固定することができない。バージョン地獄 (dependency hell) に陥ると大変になる。

cabal-install と併用し, プロジェクトごとのパッケージを固定する Stack の導入についてのメモ。

基本的なコンセプトは Ruby のための Bundler と同じ。プロジェクトが直接必要とするライブラリと, さらに依存するライブラリについて、システムグローバルにインストールされたパッケージで不足する場合, プロジェクト内にインストールしてしまう。

※実際には、ライブラリ本体は, ~/.stack/snapshots 以下にインストールされる。

Haskell は Ruby より状況が難しい。

1. パッケージの分け方が細かすぎる。2. 各パッケージのバージョン番号の振り方がよくなく、メジャーバージョンが上がってないのに非互換がしばしばある。単に依存するバージョンの最新版では、組み合わせとして壊れることがある。

Stack では, 依存関係として、テストされた組み合わせを維持する Stackage Server を利用する。GHC のバージョンごとに, Long Term Support (LTS) リリースを作っている。

インストール

システムにインストールされたGHCと基本ライブラリをできるだけ利用するようにする。

Fedora 24/25の場合:

# dnf copr enable petersen/stack
# yum install stack

Fedora 30 以降では, petersen/stack リポジトリではなく, petersen/stack2 リポジトリを入れます。

# dnf copr enable petersen/stack2
# yum install stack

/usr/bin/stack コマンドが入る。(これは Fedora リポジトリのもの)

次のコマンドで stack を最新版に上げる。~/.local/bin に最新版が入るので, あらかじめ ~/.bash_profile で、/usr/bin より先に参照されるようにすること。

$ stack upgrade

GHCバージョンを固定する

何もしないと, GHCすらプロジェクトごとにインストールしようとする。さすがにやりすぎ。

システムGHCを使う場合, ~/.stack/config.yaml ファイルに次を追加する。

system-ghc: true

ドキュメントでは resolver, compiler も指定できるようだが, stack new で考慮されない。

何か作る

stack new コマンドでプロジェクトを作る。

$ stack new hello-world new-template
Downloading template "new-template" to create project "hello-world" in hello-world/ ...

The following parameters were needed by the template but not provided: author-email, author-name, category, copyright, github-username
You can provide them in /home/horikawa/.stack/config.yaml, like this:
templates:
  params:
    author-email: value
    author-name: value
    category: value
    copyright: value
    github-username: value
Or you can pass each one as parameters like this:
stack new hello-world new-template -p "author-email:value" -p "author-name:value" -p "category:value" -p "copyright:value" -p "github-username:value"

Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- hello-world/hello-world.cabal

Selecting the best among 9 snapshots...

* Matches lts-7.15

Selected resolver: lts-7.15
Initialising configuration using resolver: lts-7.15
Total number of user packages considered: 1
Writing configuration to file: hello-world/stack.yaml
All done.

(2020.5 更新) 自動的に生成されるファイルは次のとおり;

  • .gitignore
  • ChangeLog.md
  • LICENSE
  • README.md
  • Setup.hs
  • app
    • Main.hs
  • hello-world.cabal
  • package.yaml
  • src
    • Lib.hs
  • stack.yaml
  • test
    • Spec.hs

stack setup はやりすぎ。スキップする。

stack.yaml を編集し、バージョン固定。手許では GHC 7.8.4 を使っている。対応する LTS を指定する。

(2020.5) Fedora 32 では GHC v8.6.5 が入る。対応するのは LTS 14.27.

resolver: lts-14.27

ビルド。

$ stack build

実行。実行ファイル名がダサい。

$ stack exec hello-world-exe
someFunc

依存ライブラリの追加

直接、依存するライブラリは hello-world.cabal package.yaml ファイルに追加する。hello-world.cabal ファイルは, package.yaml ファイルから自動的に生成される。

dependencies:
- base >= 4.7 && < 5

library:
  source-dirs: src

executables:
  hello-world-exe:
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - hello-world

stack build でエラーになることがある。さらに依存しているライブラリで ltsにないもの (バージョン番号が違うものも) は, プロジェクトの stack.yaml に追加。

extra-deps:
- http-api-data-0.3.3