おすすめHaskellプロジェクト作成方法(ほぼ)2021年版
はじめに
(あともう少しで)あけましておめでとうございます。
この記事では2020年12月31日時点での私のおすすめの最新のHaskellプロジェクトの作成方法をまとめます。
Haskellの環境構築はstackを使うものとcabalを使うものの2種類があります。
歴史的にはcabalの不便さを改善したのがstackですが、現在はcabalも進化して
不便さは大幅に解消されているため、筆者の好みでcabalによる環境構築をまとめます。
ツールのインストール
ghcup
ghcupはGHCとcabalとHLSをバージョン管理してくれるインストーラです。
インストールガイド を参考にインストールしてください。
バージョン確認
ghcupをインストールしたら
ghcup list
を実行しましょう。利用可能なツールのバージョンが表示されます。
$ ghcup list
Tool Version Tags Notes
✗ ghc 7.10.3 base-4.8.2.0
✗ ghc 8.0.2 base-4.9.1.0
✗ ghc 8.2.2 base-4.10.1.0
✗ ghc 8.4.1 base-4.11.0.0
✗ ghc 8.4.2 base-4.11.1.0
✗ ghc 8.4.3 base-4.11.1.0
✗ ghc 8.4.4 base-4.11.1.0
✗ ghc 8.6.1 base-4.12.0.0
✗ ghc 8.6.2 base-4.12.0.0
✓ ghc 8.6.3 base-4.12.0.0
✗ ghc 8.6.4 base-4.12.0.0 hls-powered
✓ ghc 8.6.5 base-4.12.0.0 hls-powered
✗ ghc 8.8.1 base-4.13.0.0
✓ ghc 8.8.2 base-4.13.0.0 hls-powered
✓ ghc 8.8.3 base-4.13.0.0 hls-powered
✔✔ ghc 8.8.4 recommended,base-4.13.0.0 hls-powered
✗ ghc 8.10.1 base-4.14.0.0 hls-powered
✗ ghc 8.10.2 base-4.14.1.0 hls-powered
✗ ghc 8.10.3 latest,base-4.14.1.0
✗ ghc 9.0.0.20200925 prerelease,base-4.15.0.0
✗ cabal 2.4.1.0
✗ cabal 3.0.0.0
✔✔ cabal 3.2.0.0 latest,recommended
✗ cabal 3.4.0.0-rc4 prerelease
✓ hls 0.6.0 stray
✔✔ hls 0.7.1 latest,recommended
✗ ghcup 0.1.12 latest,recommended
各行の先頭の1文字目はツールがインストールされているか、2文字目は現在有効になっているかを表します。特に理由がなければrecommended
となっているものを選びましょう。
ghcupが最新でない場合はアップグレードしておきましょう。
ghcup upgrade
ghcのインストール
GHCはコンパイラです。今回は8.4.4をインストールします。
ghcup install ghc 8.4.4
ghcup set ghc 8.8.4
cabalのインストール
cabalはパッケージマネージャです。今回は3.2をインストールします。
ghcup install cabal 3.2.0.0
ghcup set cabal 3.2.0.0
hlsのインストール
hlsはわりと最近リリースされたLanguage Server (IDEエンジン)です。今回は0.7.1をインストールします。
ghcup install hls 0.7.1
ghcup set hls 0.7.1
プロジェクトの作成
Githubレポジトリの作成
最初にGithub上でレポジトリを作っておきます。
READMEと.gitignoreを作成しておくと良いです。
作成したらローカルにcloneしましょう。
git clone git@github.com:autotaker/start-haskell.git
cd start-haskell
cabalプロジェクトの初期化
プロジェクトの初期設定を行います。
cabal update
cabal init -i
二つ目のコマンドを実行すると対話的にプロジェクトの設定ができます。
いくつかポイントを押さえておくと
- 最初の質問(Should I generate a simple project with sensible defaults?)はn
- package type: Library and Executable
- cabal spec: 2.4
- Application source directory: app
- Library source directory: src
- Test source directory: test
にしておきましょう。それ以外はデフォルト値にしておけばとりあえずは問題ありません。
以下が設定例です。
$ cabal init -i
Should I generate a simple project with sensible defaults? [default: y] n
What does the package build:
1) Executable
2) Library
3) Library and Executable
Your choice? 3
What is the main module of the executable:
* 1) Main.hs (does not yet exist, but will be created)
2) Main.lhs (does not yet exist, but will be created)
3) Other (specify)
Your choice? [default: Main.hs (does not yet exist, but will be created)] 1
Please choose version of the Cabal specification to use:
* 1) 1.10 (legacy)
2) 2.0 (+ support for Backpack, internal sub-libs, '^>=' operator)
3) 2.2 (+ support for 'common', 'elif', redundant commas, SPDX)
4) 2.4 (+ support for '**' globbing)
Your choice? [default: 1.10 (legacy)] 4
Package name? [default: start-haskell]
Package version? [default: 0.1.0.0]
Please choose a license:
1) GPL-2.0-only
2) GPL-3.0-only
3) LGPL-2.1-only
4) LGPL-3.0-only
5) AGPL-3.0-only
6) BSD-2-Clause
* 7) BSD-3-Clause
8) MIT
9) ISC
10) MPL-2.0
11) Apache-2.0
12) LicenseRef-PublicDomain
13) NONE
14) Other (specify)
Your choice? [default: BSD-3-Clause]
Author name? [default: autotaker]
Maintainer email? [default: xxxxxx@xxxxx.com]
Project homepage URL? https://github.com/autotaker/start-haskell
Project synopsis? my favorite cabal project example
Project category:
* 1) (none)
2) Codec
3) Concurrency
4) Control
5) Data
6) Database
7) Development
8) Distribution
9) Game
10) Graphics
11) Language
12) Math
13) Network
14) Sound
15) System
16) Testing
17) Text
18) Web
19) Other (specify)
Your choice? [default: (none)]
Application (Main.hs) directory:
* 1) (none)
2) src-exe
3) app
4) Other (specify)
Your choice? [default: (none)] 3
Library source directory:
* 1) (none)
2) src
3) lib
4) src-lib
5) Other (specify)
Your choice? [default: (none)] 2
Should I generate a test suite for the library? [default: y] y
Test directory:
* 1) test
2) Other (specify)
Your choice? [default: test] 1
What base language is the package written in:
* 1) Haskell2010
2) Haskell98
3) Other (specify)
Your choice? [default: Haskell2010]
Add informative comments to each field in the cabal file (y/n)? [default: n] n
Guessing dependencies...
Generating LICENSE...
Generating Setup.hs...
Generating CHANGELOG.md...
Generating src/MyLib.hs...
Generating app/Main.hs...
Generating test/MyLibTest.hs...
Generating start-haskell.cabal...
You may want to edit the .cabal file and add a Description field.
さてプロジェクト構成を確認しておきましょう。
$ tree
.
├── CHANGELOG.md
├── LICENSE
├── Setup.hs
├── app
│ └── Main.hs
├── src
│ └── MyLib.hs
├── start-haskell.cabal
└── test
└── MyLibTest.hs
3 directories, 7 files
gitにコミットしてpushしましょう。
git add .
git commit -m 'init cabal project'
git push
CIの設定
人権を得るためにGithub Actionsを設定しておきましょう。
publicレポジトリなら無料です。
Actionsタブを開くとHaskellがsuggestされているはずなので、Set up this workflow
ボタンを押します。
ghcとcabalのバージョンを変更してコミットしましょう。
しばらく待つと、CIでのビルドとテストが実行されます。
CI badgeを作っておくとちょっとかっこいいです。
生成されたURLをREADME.mdに書いてコミットしましょう。
ビルド設定
ローカルでのビルドの設定を行います。
cabal configure --enable-coverage --enable-document --enable-profiling
すると cabal.project.local
ファイルが生成されます。
このファイルはローカルでのビルド時に使われるものなのでコミットはしなくて良いです。
profiling: True
coverage: True
library-coverage: True
documentation: True
package *
documentation: True
説明しておくと、
-
profiling
: Haskellのプロファイラを有効化します。少し実行が遅くなりますが、代わりにプロファイルが取れるのと、スタックトレースがまともになりデバッグしやすくなります。 -
coverage
: 実行時にカバレッジが取れるようになります。 -
documentation
: haddockを生成します。IDEでドキュメントを表示してくれるので便利です。
そのほかのオプションは こちらを参考にしてください。
IDE設定
さて次はIDEの設定を行いましょう。
プラグインインストール
VSCodeを入れて、Haskell extensionを入れます。
ワークスペースの設定
VSCodeでcabalプロジェクトのディレクトリを開き、ワークスペース設定を開き、
Text Editor > Format on save
を有効化します。
タスクの設定
ビルドやテストをタスクとして追加します。
コマンドパレットから、Tasks: Configure Task
を選択するとtasks.json
が作成されます。
cabal build
, cabal test
を登録しておきましょう。
これでCtrl + Shift + B
(MacならCmd + Shift + B
)でビルドが走るようになります。
追加の設定
ここまでが基本的な設定でした。
ここからはちょっと開発が便利になる設定を紹介していきます。
Common stanza
cabalファイルではlibraryとtestそれぞれで依存ライブラリを記述する必要があります。
昔のcabalではベタ書きするよりほかはなかったので以下のように
ほぼ同じ設定を2回書く必要があって面倒でした。
library
build-depends:
-- long list of dependencies
test-suite start-haskell-test
build-depends:
-- long list of dependencies again
しかし今のcabalは違います。common ${セクション名}
で始まるセクションに共通の設定を書いて
各セクションの最初にimport: ${セクション名}
を書くことで設定を継承することができるようになりました。
common shared-properties
default-language: Haskell2010
build-depends:
-- list of common dependencies
library
import: shared-properties
test-suite start-haskell-test
import: shared-properties
build-depends:
-- list of test only dependencies
記載例は以下のコミットを確認ください。
詳細はこちらを参照ください。
hspec-discover
hspec
はHaskellユニットテストのデファクトスタンダードです。
hspec-discover
というツールを使うことでテストディレクトリ以下の
*Spec.hs
にマッチするモジュールを探索して、各モジュールのspec
を実行してくれます。
設定例は以下のコミットを参考にしてください。
cabal-fmt
cabalファイルにはexposed-modules
やother-modules
にモジュールのリストをベタ書きしなければなりません。ファイルを追加するたびにcabalファイルを編集するのは面倒です。
この面倒な作業を自動化してくれるツールがcabal-fmtです。
このツールは.cabal
ファイルを整形してくれるフォーマッタですが、
ディレクトリを探索して、モジュールリストを自動更新してくれる機能があります。
インストール
ユーザのホームディレクトリにインストールしておくと良いです。
cd ~
cabal install cabal-fmt
設定
cabalファイルのexposed-modules
やother-modules
の前に以下のようなコメントを挿入します。
-- cabal-fmt: expand ${ディレクトリ名}
expand
の引数に-${モジュール名}
をつけると、特定のモジュールを除外できます。
例えばhspec-discoverのSpecモジュールが追加されると困るので以下のように書きます。
-- cabal-fmt: expand test -Spec
other-modules: MyLibSpec
実行
-i
オプションをつけると存在しているcabalファイルを上書きします。
cabal-fmt -i start-haskell.cabal
タスク設定
せっかくなのでVSCodeのtaskを追加して、ビルドやテストの実行前に自動実行されるようにしておきましょう。
@@ -3,6 +3,19 @@
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
+ {
+ "label": "cabal-fmt",
+ "type": "shell",
+ "command": "cabal-fmt -i start-haskell.cabal",
+ "presentation": {
+ "echo": true,
+ "reveal": "never",
+ "focus": false,
+ "panel": "shared",
+ "showReuseMessage": true,
+ "clear": false
+ }
+ },
{
"label": "build",
"type": "shell",
@@ -19,7 +32,10 @@
"group": {
"kind": "build",
"isDefault": true
- }
+ },
+ "dependsOn": [
+ "cabal-fmt"
+ ]
},
{
"label": "test",
@@ -37,7 +53,10 @@
"group": {
"kind": "test",
"isDefault": true
- }
- }
+ },
+ "dependsOn": [
+ "cabal-fmt"
+ ]
+ },
]
}
\ No newline at end of file
設定例は以下のコミットを参考にしてください。
cradleの設定
VSCodeでHaskellのファイルを開くと以下のメッセージが出てきてうざいです。
これはhie.yaml
というHLS向けの設定ファイルがないために表示されるメッセージです。
手で書いても良いのですが、自動生成してくれるツールがあるので使いましょう。
インストール
これもユーザのホームディレクトリにインストールしておきましょう。
cd ~
cabal install implicit-hie
hie.yaml生成
cabalファイルがあるディレクトリで以下を実行してください。
gen-hie > hie.yaml
すると以下のようなファイルが生成されます。
cradle:
cabal:
- path: "src"
component: "lib:start-haskell"
- path: "app/Main.hs"
component: "start-haskell:exe:start-haskell"
- path: "test"
component: "start-haskell:test:start-haskell-test"
これをコミットしておきましょう。
Warningの設定
おっと、Warningの設定をするのを忘れていました。
@@ -26,6 +26,7 @@ extra-source-files:
common shared-properties
build-depends: base ^>=4.13.0.0
default-language: Haskell2010
+ ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -Wmissing-import-lists -Wcompat
common test-depends
build-depends: hspec ^>=2.7.4
以下を参考に -Wcompat
を追加しておきました。
(他にお勧めがあればコメントいただけると助かります。)
まとめ
以上のようになかなか設定量は多いですが、色々と設定しておくと開発が捗ります。
以下のレポジトリに今回の設定をまとめました。参考にしてみてください。
参考文献
Discussion
GitHub Actions の
actions/setup-haskell
はもうメンテナンスされていないようなのでhaskell/actions/setup
の方がいいようです。