Haskellプロジェクトを始めるにあたって
これは一人Computer Scienceアドベントカレンダー 15日目の記事です。
Computer Science何も関係ないけど大丈夫か?(まぁ一応Haskellはテーマの1つであったというアレはあるけど)
今回はHaskellで開発を始める時にいつもやってるセットアップの作業とかの説明をします。 どうも、Haskellerによるstackみたいな周辺ツールの情報の発信が足りてないんじゃないかみたいな噂が流れてきたのでじゃあまぁなんか記事にするかという流れです。
ところでstackの説明はググれば日本語の記事がそれなりにヒットするようになったと思うのでここではあんまり説明しません。
開発環境構築
このセクションは初回のみです。
Haskellのインストール
stackはプロジェクトを管理するツールっていうのかな?まぁビルドツールになったりパッケージマネージャーになったりghcを管理するのに使ったりなんかまぁそういうツールです(なんて言えばいいんだろう)。
linux系なら公式ドキュメントを見ながら次のようにするといいと思います。
$ curl -sSL https://get.haskellstack.org/ | sh
# stackのinstall
$ stack setup
# GHC(コンパイラ)を入れる
stackを入れてから stack setup
でコンパイラが入るのでのんびり待ちます。
~/.local/bin/
にパスを通しておきます。
エディタ
-
emacsの人: intero
-
spacemacsの人: haskell layer
-
IntelliJ IDEAの人: intellij-haskell
を使いましょう。その他のエディタは知らない(emacs/intellijのプラグインが特に優秀みたいなので可能ならどっちかを使うのがいいんじゃないでしょうか)。
プロジェクトセットアップ
stack new
[2017/12/16追記]
どうやらstackのデフォルトテンプレートであるnew-templateがいつの間にやらhpackを使うように変わったようです。なので、以下のsimple-hpackは不要で普通に stack new [プロジェクトの名前]
とすれば同じものが得られます1。
configuration
stack newが終わると必要なファイル群と stack.yaml
と package.yaml
が出来ます。
ここでpackage.yamlを適当に編集します(しなくても問題ないです)。
$ stack build
でビルドできればOKです。
そして開発へ
以下は必要になった時に必要になった項目を随時行います。
パッケージの追加
パッケージを追加したいときはpackage.yamlのdependenciesに追加します。 バージョンの指定とかできますが別にしなくていいです。
dependencies:
- base
- lens
- mtl
...
hackageのパッケージの追加
上のやり方で上手く行くのはパッケージがstackage(Haskellのパッケージを各バージョンごとに登録しとくところ)にある場合だけです。 hackageにパッケージがある場合はstack.yamlのextra-depsに バージョンまで含めて 書きます。
extra-deps:
- package-1.2.3.4
...
といっても、これが必要な場合はstack buildした時点でstackがこういう風に書けって教えてくれるので、それをコピペするだけでいいです。
githubのパッケージの追加
例えばgithubにしかパッケージがない場合も同じくstack.yamlのextra-depsに追加します(参照)。
extra-eps:
- git: git@github.com:hoge/piyo.git
commit: commitID
default-extensions
よく使うGHC拡張はpackage.yamlのdefault-extensionsに書いておきます。
default-extensions:
- Strict
- LambdaCase
- GADTs
- TemplateHaskell
...
まぁこの辺はお好みで。
テスト
好きなものを使えばいいと思います。 私はtastyをよく使います。tastyはtasty-hunitやtasty-quickcheckなんかがあるので色んなテストのかき方が出来たりtasty-discoverでテストを自動で検出して走らせたりできるので便利です。
package.yamlに
tests:
hoge-test:
source-dirs: test
main: Driver.hs
dependencies:
- base
- hoge
- tasty
みたいにして書いて、 test/Driver.hs
に
{-#
OPTIONS_GHC -F
-pgmF tasty-discover
-optF --tree-display
#-}
と書くと使えます。 stack test
であとは勝手にテストが走ります。便利。
stackの参照するresolverのバージョンを上げる
stackはresolverで指定されたsnapshotを常に参照します(globalでもlocalでも)。 しばらく開発しているとこれが古くなったりするので、例えば
$ stack config set resolver lts
とかするとltsの最新版にあげてくれます。
リソースファイルを含める
(例えば)executableなパッケージで実行には特定のリソースファイルが必要とします。 こういう場合はpackage.yamlのdata-filesに書きます。
data-files:
- resources/hoge.txt
ところでこのパッケージが testpackage
という名前だった場合、
library:
other-modules:
- Paths_testpackage
と書いておくと、
import Paths_testpackage
-- data-filesに書いたファイル名からそのファイルのパスを得るには次の関数を使う
-- getDataFileName :: FilePath -> IO FilePath
みたいなことが出来ます(参照)。
executableをMain.hs以外から実行する
executableでMain.hs以外から実行しようとすると怒られるかもしれませんが -main-is
オプションで回避できます。
executables:
hoge:
source-dirs:
- app
main: Run.hs
ghc-options: -main-is Run
おわりに
果たしてこういう記事が求められていたのだろうか、よくわからない(違う気がする)。
こういうのも書いてほしいっていう要望があれば追加するので言ってください。
cabalファイルを捨てたいという気持ちはまぁわかるけれどいきなりツールの使い方が変わってしまう大きな変更を入れてしまうのはどうなんだ感もありますね。せめてupgradeするときにリリースノートを表示するとかして欲しいところ。
[/追記]
さてHaskellでプロジェクトを始めます。
$ stack new [プロジェクトの名前] simple-hpack
simple-hpackというのはテンプレートの名前です。ここではhpackというツールを使っています(私が普段から使っているので)。
(以下昔話なので読まなくてもいいです) さて少しだけ説明をすると、stackという便利ツールが登場する前はみんなcabalというビルドツールを使っていました。cabalはcabalファイルの情報を読み込んでビルドをするのですが、cabalファイルはちょっと面倒な部分があったのでこれをもっと簡単に書けるようにするためにhpackというツールが最近登場しました。 hpackはpackage.yamlをcabalファイルに変換するツールですが、今はstackが公式にhpackをサポートしたので、プロジェクトでpackage.yamlに必要な情報を書くとそれがhpackによってcabalファイルに変換されてcabalによるビルドが走るというところまで全てstackが面倒を見てくれます。
正直好みですが個人的にはhpackの方が楽なのでこれを使うためにここではsimple-hpackのテンプレートを指定しています。
また、以下の説明は全てhpackのpackage.yaml前提です。cabalファイルはまた少し違うので対応表で各自調べてください。