Stack (Haskell): Could not find moduleと言われたときの対処 2018年春版

『[haskell入門 関数型プログラミング言語の基礎と実践](http://gihyo.jp/book/2017/978-4-7741-9237-6)』を読みながら写経をすると、たまにGHCから`Could not find module`と怒られるときがあります。

これは「モジュールが見つからない」という意味なので、そのモジュールが入ったパッケージを入れる必要があります。

しかし、**パッケージの入れ方は複数あり、古いやり方・新しいやり方があるようです**。
（新規プロジェクトや特にこだわりが無い場合は、「新しいやり方」でOKです）

## 例

`hoge`というプロジェクトを新規作成します。

```
$ stack new hoge
```

ここでモジュール `Data.ByteString.Char8` を使いたい、とします。

`stack new`した直後だと、~~Preludeにないモジュール~~利用可能なパッケージに含まれないモジュール（Prelude以外にもいくつかあります）を`import`するとCould not find moduleエラーが出てきます。

```
$ stack ghci
*Main> import Data.ByteString.Char8
（中略）
<no location info>: error:
    Could not find module ‘Data.ByteString.Char8’
    It is not a module in the current program, or in any known package.
```

`Data.ByteString.Char8`モジュールを使うためには、`bytestring`パッケージが必要です。

この`bytestring`パッケージを指定する方法をこれから2つ、「古いやり方」と「新しいやり方」として紹介します。

## 古いやり方: 直接 `hoge.cabal` に書く

プロジェクトの中に`(パッケージ名).cabal`というファイルがあるはずです。
例えば、`stack new hoge`でプロジェクトを作れば、`hoge.cabal`があるはずです。

そこに、必要なパッケージを`executable hoge-exe`の部分に直接書くと、とりあえずビルドは通ります。

(今は `array`, `bytestring`, `containers` の3つのパッケージを入れることにしましょう)

```
executable hoge-exe
  main-is: Main.hs
  hs-source-dirs:
      app
  ghc-options: -threaded -rtsopts -with-rtsopts=-N
  build-depends:
    base >=4.7 && <5
    , array
    , bytestring
    , containers
```

しかし、`build-depends`は他の項目にもあります(`library`,`test-suite`)。
すべてを書き換えるのは面倒です。

## 新しいやり方: `package.yaml`に書く

現在（2018年春）は、必要なパッケージをStackの`package.yaml`に書く方がよいようです。
この設定を元に、`hoge.cabal`が**自動生成**（更新）されるようになっています。

[これからのHaskellプロジェクトではcabalではなくpackage.yaml(hpack)を使いましょう - ncaq](https://www.ncaq.net/2017/07/31/)では、`hoge.cabal`と`package.yaml`の関係が次のように例えられています。

> JavaScriptに対するAltJSのようなものです

`package.yaml`では次のように書きます。

```yaml
dependencies:
- base >= 4.7 && < 5
- array
- bytestring
- containers
```

この設定で`stack build`すると、`hoge.cabal`が自動生成（更新）されます。

```
library
  hs-source-dirs:
      src
  build-depends:
      array
    , base >=4.7 && <5
    , bytestring
    , containers
（中略）
executable hoge-exe
  main-is: Main.hs
  hs-source-dirs:
      app
  ghc-options: -threaded -rtsopts -with-rtsopts=-N
  build-depends:
      array
    , base >=4.7 && <5
    , bytestring
    , containers
    , hoge
（中略）
test-suite hoge-test
  type: exitcode-stdio-1.0
  main-is: Spec.hs
  hs-source-dirs:
      test
  ghc-options: -threaded -rtsopts -with-rtsopts=-N
  build-depends:
      array
    , base >=4.7 && <5
    , bytestring
    , containers
    , hoge
```

必要なパッケージが、`hoge.cabal`の`library`, `executable`, `test-suite`の3つに反映されました。
便利ですね。

## 注意: 標準以外のテンプレートを使っている場合

`stack new`では、プロジェクトのテンプレートを指定できます。
これを使うと、たとえば「Yesod用のプロジェクト」をコマンド一発で新規作成できます。
(標準で使用できるテンプレートは`stack templates`で一覧を見られます)

しかし、標準以外のテンプレートには`package.yaml`が存在しない場合もあります。
その場合は`simple-hpack`のように、末尾に`-hpack`がついたテンプレートが使えるようです。

```
$ stack new hoge simple-hpack
```

参考: [hpack と hspec に対応した stack templates - Qiita](https://qiita.com/algas/items/e201773601eccac97e1e)

## 補足: CabalとStackの関係

Stackがなかったその昔、パッケージの管理はすべてCabalの仕事でした。
しかし、依存関係が複雑になりすぎて「Cabal hell (dependency hell)」とまで呼ばれた時代でした。

現在（2018年春）は[Stackage](https://www.stackage.org/)が、「依存関係に問題のないCabalパッケージ一式」(スナップショット)をまとめてくれています。

Stackの役割はたくさんありますが、その一つは**「スナップショットの範囲で使えるCabalパッケージ」を使ってプロジェクトをビルドする**ことです。

Stackのプロジェクトは、Cabalのパッケージを包括するものです。
(詳細: [stack.yaml vs cabal package files - The Haskell Tool Stack](https://docs.haskellstack.org/en/stable/stack_yaml_vs_cabal_package_file/))

現在では`cabal`コマンドを直接触ることは少なくなりましたが、Stackで使われているパッケージの仕組みはCabalを流用しています。

たとえば、`stack.yaml`を読むと、`resolver: lts-11.6`という項目があります。
これは「Stackageのスナップショット「[LTS Haskell 11.6 (ghc-8.2.2)](https://www.stackage.org/lts-11.6)」にあるパッケージであれば、すぐに使える」という意味になります。
（ただし、`base`パッケージ以外は、上記の方法で使うパッケージを教えてあげる必要があります）

Stackは指定されたスナップショット(resolver)を**原則として**参照します。つまり次のようにパッケージを探します。

1. スナップショットにあるパッケージは、Stackageで指定された通りのバージョンを自動的に指定する
2. スナップショットにないパッケージは、外部の依存パッケージを（バージョン番号込みで）開発者に指定してもらう
    - `stack.yaml`の`extra-deps`
    - 例: `acme-missiles-0.3`

`Could not find module`と言われたら、まずはStackageで検索して、(1.のように)使われているスナップショット内のパッケージ名を調べましょう。
ただし、たまにStackageにないパッケージもあります。その場合は、(2.のように)`extra-deps`でパッケージを明示的に指定しましょう。

以上です。