みなさん、Haskellで新しく何かを書き始める時ってどうしてますか。最近の定番ディレクトリ構成は/src
にコード、/test
にテストコードってパターンが多いですね。それ自作するの、辛くないですか。
数ヶ月前に何かを作ろうと思いcabal init
を実行した時、「もう自分で/src
と/test
ディレクトリ作って/test/Spec.hs
書くのはウンザリ」と感じ、作ろうとしていた何かをそっちのけでHaskellプロジェクトのひな形生成ツールを作り始めました。それがhiです。
例えばRubyだとbundlerというライブラリを使って、下記のように新しいプロジェクトを生成できます。
$ bundle gem foo
create foo/Gemfile
create foo/Rakefile
create foo/LICENSE.txt
create foo/README.md
create foo/.gitignore
create foo/foo.gemspec
create foo/lib/foo.rb
create foo/lib/foo/version.rb
Initializating git repo in /Users/fujimura/foo
こういうものを作りたいと思ったわけです。色々考えた結果、下記のようなデザインにすることにしました。
テンプレートをgitリポジトリにするあたりはgrunt-initというJavaScriptのライブラリを参考にしました。対話式インターフェース無し、少ないオプションなどは僕の趣味です。
結果的に、こんな感じで新しいプロジェクトを生成できるようになりました。 テストをHspecで書く場合の定番ディレクトリ構成をデフォルトテンプレートにしています。
$ hi --package-name "foo-bar-baz" --module-name "Foo.Bar.Baz"
Creating new project from repository: git://github.com/fujimura/hi-hspec.git
create foo-bar-baz/.gitignore
create foo-bar-baz/LICENSE
create foo-bar-baz/README.md
create foo-bar-baz/foo-bar-baz.cabal
create foo-bar-baz/src/Foo/Bar/Baz.hs
create foo-bar-baz/src/Foo/Bar/Baz/Internal.hs
create foo-bar-baz/test/Foo/Bar/BazSpec.hs
create foo-bar-baz/test/Spec.hs
もちろんビルド可能です。失敗するテストケースを一件入れてあります。
$ cabal test
Building foo-bar-baz-0.0.0...
Preprocessing library foo-bar-baz-0.0.0...
In-place registering foo-bar-baz-0.0.0...
Preprocessing test suite 'spec' for foo-bar-baz-0.0.0...
Running 1 test suites...
Test suite spec: RUNNING...
Foo.Bar.Baz
someFunction
- should work fine FAILED [1]
1) Foo.Bar.Baz.someFunction should work fine
expected: False
but got: True
Randomized with seed 4611685480328648939
Finished in 0.0003 seconds
1 example, 1 failure
Test suite spec: FAIL
Test suite logged to: dist/test/foo-bar-baz-0.0.0-spec.log
0 of 1 test suites (0 of 1 test cases) passed.
テスト無し、フラットなディレクトリ構成のテンプレートもあります。
$ hi-m Bar -p bar -r [email protected]:fujimura/hi-flat.git
Creating new project from repository: [email protected]:fujimura/hi-flat.git
create bar/.gitignore
create bar/LICENSE
create bar/Main.hs
create bar/Bar.hs
create bar/README.md
create bar/bar.cabal
テンプレートは自作可能です。Making your own project templateを参照ください。
以下、実装時のこぼれ話です。
コーディング規約はtibbe/haskell-style-guideを踏襲しました。が、あんまり厳密にはやっていません。いわゆるfull importはしないようにしています。自分で書いたコードを読むにあたっても圧倒的にわかりやすかった。
Hackageには、tarballを作ってcabalのコマンドからアップロードするのですが、その前に一応出来たものが正しく動くかテストを実行したいところです。一連の作業をやってくれる便利なスクリプトがHspecのリポジトリにあったので有難く真似させて頂きました。
エディタはVimを使っています。ghc-mod + syntasticで保存されたらコンパイルしてエラーがあれば表示されるようにしてます。あとstylish-haskellでコードのフォーマットをしてます。これはVimからで実行できます。
最初スケッチ的な実装をした後、TDDで書き直しました。私、Haskellで関数ごとに細かいユニットテストをするのは割に合わないという認識でして、今回はいちばん外側からのみテストする戦略をとりました。具体的にはテスト用ディレクトリの中で実際に$ hi
を実行し、結果を検証するという方法です。テスト後のディレクトリのお掃除などは自分で頑張っていたのですが(bracket_を使えばOK)、途中でHspecにbeforeなどが入ったのでそれを使いました。タイミング良かった。最終的にはメインの関数だけは個別にテストするようにしました*。言うまでもなくこの戦略だと並列実行するとテストがコケるのでそこは残念です。今後は外側からのテストは最小限にしたい。
他にも色々な苦労と迷走がありました。モジュール構成が変、テンプレートに依存したテストケースが多い、など未解決のしょぼいポイントも多々あります。追加予定の機能もいくつか。興味がある方はコードを読んでみてください。リポジトリはhttps://github.com/fujimura/hiです。