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

/usr/bin/stack コマンドが入る。

最新版に上げる。

$ 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.

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

src/Lib.hs test/Spec.hs hello-world.cabal app/Main.hs stack.yaml LICENSE Setup.hs

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

stack.yaml を編集. 手許では GHC 7.8.4 を使っている。対応する LTS を指定する。

resolver: lts-2.22

ビルド。

$ stack build

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

$ stack exec hello-world-exe
someFunc

依存ライブラリの追加

直接、依存するライブラリは hello-world.cabal に追加する。

executable hello-world-exe
  hs-source-dirs:      app
  main-is:             Main.hs
  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
  build-depends:       base
                     , hello-world
                       -- ここに追加
                     , persistent
  default-language:    Haskell2010

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

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