Haskellのstackによるプロジェクトについて

# この記事について
一、二ヵ月ほど前にHaskellに入門したばかりの著者です。そのためHaskellを学ぶためにいろいろな本を読んだりサイトを見たりして学んでいるのですが、どうもHaskellの文法や関数型、モナドについてばかりの情報がほとんどでプロジェクトを作成した後についての操作法がなかなか見つかりません。そこで少しの情報を基にいろいろ試行錯誤しました。それでわかってきたことを初心者ながらに説明しようと思い、これを書いています。そしてこの記事は[Haskell (その4) Advent Calendar 2017](https://qiita.com/advent-calendar/2017/haskell4)の16日目の記事です。

# プロジェクトの作成
まずプロジェクトを作ります。そのためにコマンドプロンプト上でプロジェクトを作りたいディレクトリの直下に移動してください。そこで```stack new <projectname> [template]``` コマンドを使います。（この記事では"<>"で囲まれたものを使用者毎に入力してもらうコマンドとし、"[]"で囲まれたものを省略可能なコマンドとして扱うことにします。）ただし```projectname```には作成したいプロジェクト名、```template```は使用するテンプレートです。このテンプレートは```stack templates```とすると確認することができるテンプレートの中から選択できます。デフォルトでは```simple```を使用することになっています。今回は```stack new testProject```コマンドによりプロジェクトを作成してこれを基に説明していきます。
コマンドの実行後、次のようなファイル群が作られているのが確認できます。

- testProject
 - app
     - Main.hs
 - src
     - Lib.hs
 - test
     - Spec.hs
 - .gitignore
 - ChangeLog.md
 - LICENSE
 - package.yaml
 - README.md
 - Setup.hs
 - stack.yaml
 - testProject.cabal

これらのうち```.gitignore```、```ChangeLog.md```の二つは```git```を使い始めたら出てくるようになり、```package.yaml```は最近突然に生成されるようになっていたので出ない人もいるかも入れません。また```~/sr/config.yaml```で

```yaml:config.yaml
templates:
  scm-init: git
  params:
    author-name: <Your Name>
    author-email: <youremail@example.com>
    category: <Your Projects Category>
    copyright: <'Copyright (c) 2017 Your Name'>
    github-username: <yourusername>
```
と[ここ](https://docs.haskellstack.org/en/stable/yaml_configuration/#templates)を参考に編集すると```.git```ファイルも同時に生成してくれるようになりGitをすぐに使えるようになります。

# 生成されたファイルやフォルダの紹介
## appフォルダ
このフォルダは実行用のファイルを入れておくところです。なので生成されていた```Main.hs```は実行用のファイルです。実際に```main```関数が書いてあります。中身を確認してみます。

```haskell:Main.hs
module Main where

import Lib

main :: IO ()
main = someFunc

```
まず```Main```モジュールを宣言していますね。（なぜここでモジュールとして扱うように宣言しているのか著者にはわかりません）そして```src```フォルダにある```Lib```モジュールを使うように宣言しています。（ここで注意したいのは```Lib.hs```ファイルを使うという宣言ではなく```Lib```モジュールを使うという宣言なので```src/Lib.hs```ファイルのことだと勘違いしないこと。著者は最初勘違いして少し泥沼にはまった。）この```Lib```モジュールは```Lib.hs```ファイルにて宣言されています。
次に```main```関数の型を宣言してから本体を宣言しています。Haskellには強力な型推論が備わっているので型の宣言は特に必要ないのですが、トップレベルだからあった方がいいということで```main```関数ですが宣言されているようです。そしてこの```main```関数で使う```someFunc```の実装は```Lib```モジュールに書かれています。そのモジュールについて書いてある```Lib.hs```ファイルで説明するのでここでは説明しません。

## srcフォルダ
このフォルダは```app```フォルダ内で使う「道具」としての実装を記述したファイルを入れておくところです。主にモジュールを置くことになると思います。なので```Lib.hs```のようにじかにモジュールとしてファイルが置いてあったり、新しく```Data```フォルダを作ってその下にモジュールを作って階層分けして置いてあったりして使われます。このようなことから```main```関数が書かれているファイルが存在しないのはすぐにわかります。(逆に考えると```main```関数があるファイルだけでプロジェクトを作るならこの```src```フォルダは使わないことになる。)
では、```src```フォルダ内に生成された```Lib.hs```ファイルを見てみましょう。

```haskell:Lib.hs
module Lib
    ( someFunc
    ) where

someFunc :: IO ()
someFunc = putStrLn "someFunc"
```
まず私は```Lib```モジュールで```someFunc```関数だけ外に見せると宣言しています。そして```someFunc```関数の型宣言をしたのち実装を定義しています。自動生成直後ではただ「someFunc」と表示するだけみたいです。

## testフォルダ
このフォルダはプロジェクトのテスト関係のファイルを置くところです。著者がHaskellのテストについてまだ何もわからないので説明はできません。なので説明は省略させていただきます。

## LICENSE / README.md / Setup.hs / stack.yamlファイル
```LICENSE``` 、```README.md```ファイルは名前の通りのファイルです。```LICENSE```ファイルはある方法でどんなライセンスを使うか変更できるようです。（おそらく後で紹介する```package.yaml```に記述するものかと思われる。）
```Setup.hs```ファイルは```stack new```コマンドによってどんなプロジェクトが作られたのかの情報が書かれています。```stack.yaml```ファイルにはそのプロジェクト固有のオプションが書かれている（？）みたいです。正直Haskell初心者である著者には何もわかりませんが小さいプロジェクトを作って実行するまでいじる必要はないものだと思われるので無視しても構いません。

## package.yaml / testProject.cabalファイル
```package.yaml```ファイルは ```Hpack```（？）が使用可能なときに生成されるらしいファイルで、ビルド時に```testProject.cabal```を自動生成してくれるパッケージ管理ファイルです。どちらのファイルも「#」以降の同行の文字列はコメントになります。先に同じパッケージ管理ファイルの```testProject.cabal```について説明します。
### testProject.cabalについて
正直前半はプロジェクトに関する説明なので、見れば何を意味しているのか分かるので省略します。この```cabal```ファイルでは複数指定する場合は「,」で区切ります。
以下に後半の```library```からを表示します。

```testProject.cabal
library
  hs-source-dirs:
      src
  build-depends:
      base >= 4.7 && < 5
  exposed-modules:
      Lib
  default-language: Haskell2010

executable testProject-exe
  main-is: Main.hs
  hs-source-dirs:
      app
  ghc-options: -threaded -rtsopts -with-rtsopts=-N
  build-depends:
      base >= 4.7 && < 5
    , testProject
  default-language: Haskell2010

test-suite testProject-test
  type: exitcode-stdio-1.0
  main-is: Spec.hs
  hs-source-dirs:
      test
  ghc-options: -threaded -rtsopts -with-rtsopts=-N
  build-depends:
      base >= 4.7 && < 5
    , testProject
  default-language: Haskell2010
```

まず```library```に関しての説明をします。これは```src```フォルダと関係しています。何を言ってるかわからないと思うので説明の為、上から見ていこうと思います。
まず```hs-source-dirs```は自身の作成するHaskellのソースコードを置いておくところです。これにより```src```フォルダが認識されその中の```Lib.hs```ファイルも認識できるようになります。
次に```build-depends```についてです。これは以下でも何回も出てくる設定です。文字通り「ビルド時に依存するパッケージ」についての設定となります。今回は```library```での設定なので```src```の下にあるファイルが使うモジュールをここに記述していきます。例えば```Cotrol.Lens```を使うときは```lens```を追記します。（ここで指定するのはパッケージ名）また、最初から入力されている```base <= 4.7 && < 5```はHaskellのどのベースパッケージを使うかを意味しています。これによりパッケージの依存関係の解消時に使用できるスナップショットの範囲が大体決まります。パッケージが古いものの場合```base```があっていないこともあるかもしれないのでその時は[Hackage](https://hackage.haskell.org/)でパッケージを検索して確認してみてください。
次は```exposed-modules```についてです。ここでは外部にさらされる（exposed）とき、すなわち```app```フォルダや```src```フォルダ内のファイルが同フォルダ内のモジュールを使用するときに使いたいモジュール名を書きます。生成直後では```app```フォルダ内の```Main.hs```が```Lib```モジュールを使用するので```Lib```が書かれています。
最後の```default-language```についてですがHaskellの歴史上HaskellにはHaskell 1.0, Haskell 98, Haskell′, Haskell 2010の四つのバージョン名がついているのでどのHaskellを使うかについて書くところになります。著者の環境ではHasell 2010で生成されており最新ですね。
以下の説明ではかぶるところは適宜省略していきます。
次に```executable```について説明します。基本同じですね。しかし```library```とは違いすぐ横に```testProject-exe```と書かれています。これはビルドしたときに出来上がる実行ファイル名を意味しています。つまりこのままビルドすると```testProject-exe.exe```（著者はWindowsを使っているのでexeファイルとしている。）が出来上がることになります。（いやなら好きなように変えてしまいましょう。）この関係は後で説明する```test-suite```と```testProject-test```でも（著者はHaskellのテスト関係は全然わからない為）同じだと思われます。
そして```main-is```についてですが名前からすると```main```関数を持つファイル名の一覧か最初に実行するファイルの名前です。（著者は複数の実行用ファイルを使ったことがないのでわかりません。）しかし、初心者時点では単に```main```関数があるファイルの名前だと思っても差し支えなさそうです。そしてそのファイルが書かれているフォルダを指定するのが次の```hs-source-dirs```です。
次の```ghc-options```ではstackで```stack build```コマンドを実行時に使用するghcのオプションとして設定するオプションを指定できます。
次は```build-depends```について説明します。ここに書くのは```library```で書くのと**考え方は**基本同じ（つまり```app```以下で使用する外部パッケージについて書く）ですが、一つだけ違います。それは```testProject```の記述です。これにより```library```で記述した```exposed-modules```オプションにおけるモジュール（今でいうと```Lib```モジュール）が使用可能になります。
最後に```test-suite```について説明します。と言いたいところですが、```type```以外は他と同じようなものであり、その唯一違う```type```について著者はわからないので説明できません。
### package.yaml について
```testProject.cabal```を自動生成する似たような書き方の ```package.yaml```で、実際に書き方も似ています。ただ区切りが「,」ではなく「-」となることには注意しておいてください。
```pacage.yaml```ファイルも```testProject.cabal```ファイル同様に前半はそのプロジェクトの情報なので省略し、後半だけ説明します。
後半を以下に表示します。

```yaml:package.yaml
dependencies:
- base >= 4.7 && < 5

library:
  source-dirs: src

executables:
  testProject-exe:
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - testProject

tests:
  testProject-test:
    main:                Spec.hs
    source-dirs:         test
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - testProject
```
見てすぐにわかるように```executables```、```tests```の内容は```testProject.cabal```と同じです。また、名前が複数形になっておりインデントの深さからわかるように同様に```testProject-○○```以下をそれぞれインデントの深さに気をつけて下につなげていけば複数いけるみたいです。（情報としては聞いたことがあるが著者はやったことがないため。）
さて、```library```において```exposed-modules```オプションがなくなっていることにお気づきでしょうか。これは自動で```library```の```source-dirs```から探してくれるので不要になったようです。なお、同じく消えている```library```の```dependencies```は、自動生成時には生成されたプロジェクトのファイルの都合上その時には必要ないので削除されているだけ見たいです。なので自分で新しくオプションとして書いても問題がないようです。（その証拠に下に記したビルドの例がある。）
最後に説明の都合上順番が逆になってしまいましたが、一番上にある```dependencies```について説明します。これは```library```、```execuables```、```tests```で共通して依存するものを書くときに使われます。自動生成時には```testProject.cabal```でも何回も書かれていた```base```について書いてあることからもわかるかと思います。

# プロジェクトのビルド
普通にビルドするだけではつまらない（新しめのstackで```stack build```をすれば問題なく終わるため。）ので、少し編集してビルドしてみます。今回は以下のように編集してみることにしました。

```haskell:Lib.hs
{-# LANGUAGE TemplateHaskell #-}

module Lib
    ( someFunc
    ) where

import Control.Lens

someFunc :: IO ()
someFunc = putStrLn "someFunc"
```
標準に入っていない```Lens```パッケージを使ってみることにしました。
まずこの状態で```stack build```とコマンドプロンプトに入力してみましょう。多分コンパイラに「```Control.Lens```て何？知らないよ？」と怒られるでしょう。なので、```stack install lens```としてパッケージlensをインストールしておきます。するとそのプロジェクト内でlensパッケージが使えるようになります。（ここでlensパッケージとしたのは```Control.Lens```モジュールが所属するパッケージ名がlensであるため（[hackage](https://hackage.haskell.org/package/lens)参照））lensパッケージは大きいパッケージなので少し時間がかかるので少し待ちましょう。
インストール終了後、もう一度```stack build```してみましょう。すると先とは違ったエラーが出てきます。そこには「その```Control.lens```モジュールが書かれたパッケージ見つかったはいいが```testProject.cabal```ファイルに記述されていない。 」というようなエラーのようです。ならば記述してやりましょう。今回は著者の環境では```testProject.cabal```が自動生成されて効果を確認できないため```package.yaml```に書きます。今回は```src```フォルダ内でしか使っていないので```library```オプションに書き、以下となりました。（```dependencies```に```- lens```を追記しても問題なし。）このとき「-」の後には空白を開けておかないとパースエラーになってしまうので注意してください。

```yaml:package.yaml
library:
  source-dirs: src
  dependencies:
    - lens
```
今回の例の作成時には著者の環境で```stack build```したときに```lens-4.15.4```がほしいといわれていました。ですが、```package.yaml```が問題が起きないように自動的にバージョンを指定してくれるのでバージョン指定をしなくても大丈夫です。
そして保存してからもう一度```stack build```すると今度は問題なくビルドがとおって実行ファイルができます。
実は```stack install lens```コマンドを使わなくてもいきなり```package.yaml```に記述するとビルド時にインストールしてからビルドをしてくれます。便利です。

# 作成した実行ファイルを実行する
ビルドしたファイルは生成されている```.stack-work```フォルダ内の深いところにあります。これはビルド時のメッセージを読めば場所はすぐにわかるのですが、いちいち探すのもめんどくさいのでstackには実行に関する二つのコマンドがあります。ここではそのコマンドを説明します。
実行に関係するコマンドは二つあります。```stac exec <fiileName>```と```stack install <projectName>```の二種類です。（余談 ： 実行ファイルがもしファイルを作成したらファイルがどこにできるか、まだわからないのでわかる人教えてください）

## ```stac exec <fiileName>```
その場で```fileName```から始まる実行ファイルを実行します。今回説明時に変更した以外のところを何も手を加えなければ```stack exec testProject-exe```で```someFunc```と表示されます。

## ```stack install <projectName>```
```stack install testProject```とコマンドを入力すると```.stack-work```フォルダ内からプロジェクトの成果物がインストールされます。場所はコマンドを実行したときに表示されるのですが、決まって同じところのようなのでパスを通しておくと、コマンド（例えば```testProject-exe.exe```）ですぐに実行できるのでよさそうです。
さて、コピー先のフォルダを見てみると```testProject-exe```を名前に持つ実行ファイルが見つかります。なのでそれを実行してみましょう。（ダブルクリックや、コマンドプロンプト上で```testProject-exe.exe```と打つかドラッグアンドドロップしてエンターして実行させましょう。）すると問題なく```someFunc```と表示されます。

# 締めの言葉
以上で説明は終わりになります。だらだら長いわかりにくい文章を最後まで読んでいただきありがとうございました。以下は雑談のような蛇足のようなものなので読んでいただかなくても構いません。

# 雑談のような何か
ごく最近Haskellに入門したばかりでCabalヘルなどを知らないし、Haskellの開発環境事情やGUI事情、テスト事情なども全くわからない著者です。（教えていただけるとありがたいです。）そんな著者ですが、この記事を書こうと思った原因があります。それはtwitterで「Haskellを広めたいならstack等の周辺ツールについての説明を増やすべき」と発言したことです。これが著者が思っていたよりも多くの賛同が得られてしまいました。なので「他人に命令するなら自分も何かしろ」ってことでこの記事を初心者ながら書いてみました。Haskellを広めたいと思っている人は案外こういったHaskellの周辺の方が需要あるんだよってことを頭の片隅に置いておいてくださるとありがたいです。共感した方はどうか周辺ツールについての説明をお願いします。