『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.cabal
とpackage.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.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
補足: 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)を原則として参照します。つまり次のようにパッケージを探します。
- スナップショットにあるパッケージは、Stackageで指定された通りのバージョンを自動的に指定する
- スナップショットにないパッケージは、外部の依存パッケージを(バージョン番号込みで)開発者に指定してもらう
-
stack.yaml
のextra-deps
- 例:
acme-missiles-0.3
-
Could not find module
と言われたら、まずはStackageで検索して、(1.のように)使われているスナップショット内のパッケージ名を調べましょう。
ただし、たまにStackageにないパッケージもあります。その場合は、(2.のように)extra-deps
でパッケージを明示的に指定しましょう。
以上です。