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

More than 1 year has passed since last update.

haskell入門 関数型プログラミング言語の基礎と実践』を読みながら写経をすると、たまに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では、hoge.cabalpackage.yamlの関係が次のように例えられています。


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


package.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.caballibrary, executable, test-suiteの3つに反映されました。

便利ですね。


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

stack newでは、プロジェクトのテンプレートを指定できます。

これを使うと、たとえば「Yesod用のプロジェクト」をコマンド一発で新規作成できます。

(標準で使用できるテンプレートはstack templatesで一覧を見られます)

しかし、標準以外のテンプレートにはpackage.yamlが存在しない場合もあります。

その場合はsimple-hpackのように、末尾に-hpackがついたテンプレートが使えるようです。

$ stack new hoge simple-hpack

参考: hpack と hspec に対応した stack templates - Qiita


補足: CabalとStackの関係

Stackがなかったその昔、パッケージの管理はすべてCabalの仕事でした。

しかし、依存関係が複雑になりすぎて「Cabal hell (dependency hell)」とまで呼ばれた時代でした。

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

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

Stackのプロジェクトは、Cabalのパッケージを包括するものです。

(詳細: stack.yaml vs cabal package files - The Haskell Tool Stack)

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

たとえば、stack.yamlを読むと、resolver: lts-11.6という項目があります。

これは「Stackageのスナップショット「LTS Haskell 11.6 (ghc-8.2.2)」にあるパッケージであれば、すぐに使える」という意味になります。

(ただし、baseパッケージ以外は、上記の方法で使うパッケージを教えてあげる必要があります)

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


  1. スナップショットにあるパッケージは、Stackageで指定された通りのバージョンを自動的に指定する

  2. スナップショットにないパッケージは、外部の依存パッケージを(バージョン番号込みで)開発者に指定してもらう



    • stack.yamlextra-deps

    • 例: acme-missiles-0.3



Could not find moduleと言われたら、まずはStackageで検索して、(1.のように)使われているスナップショット内のパッケージ名を調べましょう。

ただし、たまにStackageにないパッケージもあります。その場合は、(2.のように)extra-depsでパッケージを明示的に指定しましょう。

以上です。