Stackでやる最速Haskell Hello world! (GHCのインストール付き！)

昨日[第23回Haskellもくもく会 @ 朝日ネット](http://haskellmokumoku.connpass.com/event/18236/)で初めて[stack](https://github.com/commercialhaskell/stack)を触ったのですが、
あまりにも簡単・高速にパッケージ作りの準備ができたので、やったことを共有したいと思います。
GHC(Haskellの最も有名なコンパイラ)のインストールまでやってくれるので、**これからHaskell始めます！みたいな人にもオススメ**です。

stack自体が何かは @tanakh さんの[Haskellのビルドツール"stack"の紹介](http://qiita.com/tanakh/items/6866d0f570d0547df026)をご覧ください。
一言で言うと「GHCのインストールからパッケージのインストールにビルドまで、ワンストップでやってくれる神ツール」ですかね。

# インストール

各OSごとに微妙に違うみたいなので、[公式ページの解説](https://docs.haskellstack.org/en/stable/README/#how-to-install)をご覧ください。
とはいえ、基本的には実行ファイルを`PATH`のどこかに置くだけなので、この手の作業に慣れている人には朝飯前でしょう。
1ファイルだけでインストールできちゃうのも魅力ですね！

## :warning: すでにcabalをお使いの方へ

くれぐれも`cabal install stack`は**しない**でください。
あれはstack自身の開発者向けのパッケージです。
`cabal`の難しいところをカバーするために`stack`があるのですから、`cabal`のことは一旦忘れましょう。

# プロジェクトディレクトリーの作成

`stack new`コマンドで適当なプロジェクト名を指定すれば、適当なプロジェクトテンプレートが入ったディレクトリーを作ってくれます。
第2引数にテンプレートの名前を指定すればもっとよいものを提供してくれるみたいですが、今回はとりあえず簡単に。

```shell-session
$ stack new foobar-project
Downloading template "new-template" to create project "foobar-project" in foobar-project/ ...
Using the following authorship configuration:
author-email: example@example.com
author-name: Example Author Name
Copy these to /home/yu/.stack/stack.yaml and edit to use different values.
Writing default config file to: /home/yu/tmp/foobar-project/stack.yaml
Basing on cabal files:
- /home/yu/tmp/foobar-project/foobar-project.cabal

Checking against build plan lts-3.2
Selected resolver: lts-3.2
Wrote project config to: /home/yu/tmp/foobar-project/stack.yaml

$ cd foobar-project/
$ ls
LICENSE  Setup.hs  app  foobar-project.cabal  src  stack.yaml  test
```

## stack 1.6.1未満を使っている場合のエラー

:warning: 手元で試したところ、`stack`のバージョンが1.5.1などのバージョンの場合、下記の`AesonException`が起こるようです。

```
$ stack new hoge
Downloading template "new-template" to create project "hoge" in hoge\ ...
Initialized empty Git repository in C:/Users/igrep/Downloads/tmp/hoge/.git/
Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- hoge\hoge.cabal

Selecting the best among 12 snapshots...

Downloaded lts-10.0 build plan.
AesonException "Error in $.packages.cassava.constraints.flags['bytestring--lt-0_10_4']: Invalid flag name: \"bytestring--lt-0_10_4\""
```

[問題のIssue](https://github.com/fpco/stackage/issues/2759#issuecomment-353373881)でSnoyman氏が述べているように`stack upgrade`したり、（Chocolateyでstackを入れた場合は）`choco upgrade haskell-stack`するなどして、stackのバージョンを上げてください。
あるいは、ちょっと試せてないのですが、事情があってstackのアップデートができない場合、`stack --resolver=lts-9.20`のように古いLTS Haskellを指定するという方法でもよいかも知れません（古いバージョンのGHCがインストールされてしまうので、推奨はしませんが）。

# とにかくビルドしましょう

こちらのテンプレート、何もしなくてもとりあえずすでにビルドできる状態になっています。
文字通り`stack build`を実行してください。

```shell-session
$ stack build
No GHC found, expected version 7.10.2 (x86_64) (based on resolver setting in /home/michael/helloworld/stack.yaml).
Try running stack setup
```

おっと、「何もしなくても」はさすがに大袈裟でした。 :sweat:
上記のように、ここで必要なバージョンのGHCがインストールされていない場合、エラーになります。
[@tanakhさんの記事](http://qiita.com/tanakh/items/6866d0f570d0547df026#stack-setup)の時点では`stack build`だけで自動的にインストールまでしてくれる仕様だったようですが、あまりにもいきなりすぎると判断したんでしょう。
とはいえ、ちゃんとエラーメッセージに`stack setup`してくれ、と親切にも書いてくださっているので、素直に実行しましょう。
自動的に最初にこの記事を書いた時点における最新安定版のGHC 7.10.2が入ります。

:memo: 2017/12/09 追記: まだ手元で試せてはいないのですが、[Stack 1.6.1](https://github.com/commercialhaskell/stack/releases/tag/v1.6.1)から、デフォルトで、`stack build`しただけでGHCのインストールも行うようになったそうです。なので`stack setup`は不要になった模様です。

```shell-session
$ stack setup
Downloaded ghc-7.10.2.
... 省略 ...
Installed GHC.
stack will use a locally installed GHC
For more information on paths, see 'stack path' and 'stack exec env'
To use this GHC and packages outside of a project, consider using:
stack ghc, stack ghci, stack runghc, or stack exec
```

ネットワークの状況にも依存するとは思いますが、GHCのインストールはやっぱり結構時間がかかりました。
待ちましょう。 :sleeping:

インストールが終わったら、気を取り直して`stack build`。

```shell-session
foobar-project-0.1.0.0: configure
Configuring foobar-project-0.1.0.0...
foobar-project-0.1.0.0: build
Preprocessing library foobar-project-0.1.0.0...
[1 of 1] Compiling Lib              ( src/Lib.hs, .stack-work/dist/x86_64-linux/Cabal-1.22.4.0/build/Lib.o )
In-place registering foobar-project-0.1.0.0...
Preprocessing executable 'foobar-project-exe' for foobar-project-0.1.0.0...
[1 of 1] Compiling Main             ( app/Main.hs, .stack-work/dist/x86_64-linux/Cabal-1.22.4.0/build/foobar-project-exe/foobar-project-exe-tmp/Main.o )
Linking .stack-work/dist/x86_64-linux/Cabal-1.22.4.0/build/foobar-project-exe/foobar-project-exe ...
foobar-project-0.1.0.0: install
Installing library in
/home/yu/tmp/foobar-project/.stack-work/install/x86_64-linux/lts-3.2/7.10.2/lib/x86_64-linux-ghc-7.10.2/foobar-project-0.1.0.0-E05fmeQhYiF0vVxMlsqLP6
Installing executable(s) in
/home/yu/tmp/foobar-project/.stack-work/install/x86_64-linux/lts-3.2/7.10.2/bin
Registering foobar-project-0.1.0.0...
```

# さぁ実行だ！

`stack exec`を使えば、プロジェクトの設定(`stack.yaml`)毎に空気を読んで`PATH`を適切に設定してくれます。
デフォルトのテンプレートでビルドした実行ファイルは`<プロジェクト名>-exe`という名前になってますので、
今回の場合は`stack exec foobar-project-exe`しましょう。

```shell-session
$ stack exec foobar-project-exe
someFunc
```

...お、惜しい。`Hello, world!`じゃない... :cry:
せっかくなんでプロジェクトの構造を確認しつつ、`Hello, world!`を出すよう編集しましょう。
お好きなエディタで`app/Main.hs`を開いてください。

```haskell:app/Main.hs
module Main where

import Lib

main :: IO ()
main = someFunc
```

ここではHaskell自体の詳細は割愛しますが、この`app/Main.hs`の`main`という関数が、`stack exec foobar-project-exe`した時に最初に実行される関数です。
この場合、`Lib`というパッケージから（暗黙に）`import`された、`someFunc`という関数を呼んでいます。

それでは`Lib`の`someFunc`を覗いてみましょう。`src/Lib.hs`にその正体があります。

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

someFunc :: IO ()
someFunc = putStrLn "someFunc"
```

さっき`stack exec foobar-exe`した時に出力された`someFunc`という文字列はここにありました。
どうやら`putStrLn`という関数で指定した文字列を出力していたようですね。
ここを我らが（？）`Hello, world!`に書き換えましょう。それだけです。

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

someFunc :: IO ()
someFunc = putStrLn "Hello, world!"
```

後はもう一度`stack build`して実行するだけ！

```shell-session
$ stack build
... 省略 ...
$ stack exec foobar-exe
Hello, world!
```

できました！これであなたもHaskellerです！ :white_check_mark:

# この先はどうする？

あまりにもstackが簡単だったので勢いで導入を書いてしまいました。
誰か入門を考えてください...。 :anguished:
[すごいH本](http://shop.ohmsha.co.jp/shop/shopdetail.html?brandcode=000000001926&search=978-4-274-06885-0&sort=)は最初`ghci`から入るので、この記事の入門では不向きなのです...。

# 残りは気になったら読んでください。

## 他のパッケージを入れたい時は？

例えばおなじみ「[すごいH本]((http://shop.ohmsha.co.jp/shop/shopdetail.html?brandcode=000000001926&search=978-4-274-06885-0&sort=))」を読み進めたら出てくる、`Data.Map`を`import`したくなったとしましょう。

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

import qualified Data.Map as M

someMap :: M.Map String String
someMap = M.fromList [("stack exec", "Hello, world!")]

someFunc :: IO ()
someFunc = print $ M.lookup "stack exec" someMap
```

すごいH本には「最初から入っているから大丈夫だよ〜」と書いてありますが、ここで紹介したテンプレートで使用しようとすると、残念ながらそのままではエラーになってしまいます。

```shell-session
$ stack build
foobar-project-0.1.0.0-42e96c77117d3dac75f022f842817f66: unregistering (local file changes)
foobar-project-0.1.0.0: build
Preprocessing library foobar-project-0.1.0.0...

/home/yu/tmp/foobar-project/src/Lib.hs:5:18:
    Could not find module ‘Data.Map’
    It is a member of the hidden package ‘containers-0.5.6.2@conta_LKCPrTJwOTOLk4OU37YmeN’.
    Perhaps you need to add ‘containers’ to the build-depends in your .cabal file.
    Use -v to see a list of the files searched for.
```

ここでstack...ではなく、stackがラップしているパッケージマネージャー、cabal（これまでHaskellを使ってきた方にはおなじみですね）に、
「`Data.Map`が入ってる`containers`っていうパッケージを使いたいんだけど！！」と教えてあげましょう。
`foobar-project.cabal`というファイル（通称cabalファイルと呼びます）... ではなく、`package.yaml`というファイルを開いてください。  

:warning: 2017/12/23 追記: どうやら、`stack new`した場合のデフォルトのテンプレート`package.yaml`が含まれるようになったようです。これによって、cabalファイルを編集しても、`stack build`した時点で書き換えられてしまいます。詳しくは[この記事へのコメント](https://qiita.com/igrep/items/da1d8df6d40eb001a561#comment-97a67ca5f58bed5acfc3)とその回答をご覧ください。

こんな↓感じのファイルになっています。

```yaml:package.yaml
name:                foobar-project
version:             0.1.0.0

... 省略 ...

dependencies:
- base >= 4.7 && < 5

library:
  source-dirs: src

... 省略 ...
```

上記の`dependencies:`と書かれた行の下に、次のように追記してください。

```yaml:package.yaml
name:                foobar-project
version:             0.1.0.0

... 省略 ...

dependencies:
- base >= 4.7 && < 5
- containers # <= ココ！

... 省略 ...
```

上の`base >= 4.7 && < 5`と書かれた行のように、**特にバージョン番号を指定する必要はない**ようです。
stack自身が用意した`lts-10.0`というバージョンのStackage（安定してビルドできるバージョンを記録したパッケージ集）から適切なバージョンを選んでくれるからでしょう。
自前でバージョン指定を考えて首をひねるのは、もう過去の出来事です [^maintainers]。

[^maintainers]: もちろん、そうやってできるのは[Stackage](http://www.stackage.org/)をメンテされているみなさんがいらっしゃるからなのです。感謝しましょう。それから、Hackageにアップロードする予定のプロジェクトでは、せめてlower boundくらいは書いた方が親切かと思います（私自身もちゃんとできてませんが...）

さて、`package.yaml`を編集したら、もう一度`stack build`するだけで、新しいパッケージをとってきてくれます。

```shell-session
$ stack build
... 省略 ...
$ stack exec foobar-project-exe
Just "Hello, world!"
```

これでカンペキ！ :satisfied:

### もっと他のパッケージを入れたいときは？

冒頭でも触れた、[@tanakhさんの記事](http://qiita.com/tanakh/items/6866d0f570d0547df026#%E7%B0%A1%E5%8D%98%E3%81%AA%E4%BD%BF%E3%81%84%E6%96%B9)をご覧ください。

## foobar-project-exe とか実行ファイルの名前ダサすぎなんですけど？ :rage2:

こちらもstackがラップしている、cabalの設定によります。`package.yaml`を開いて...

```package.yaml
  ... 省略 ...

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

... 省略 ...
```

下記のように、`executables:`の下の方にある`foobar-project-exe:`と書かれた行を、`<好きな実行ファイル名>:`に変え、`stack build`し直してください。
Windows向けに拡張子`.exe`は特に言わなくても付けてくれたはず。

```package.yaml
  ... 省略 ...

executables:
  your-cool-command: # <- ココ！
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - foobar-project

... 省略 ...
```

## インストールしたものはどこに行くの？

まだまだ激しく開発中のツールなので今後仕様が変わるかもですが、私の環境ではこんな感じの場所にインストールされました。
だいたいみなさんのホームディレクトリーや`%USERPROFILE%\AppData\Local`以下あたりのそれっぽいディレクトリーに読み替えていただければ見つかるでしょう。

- `stack install`した、プロジェクト固有の実行ファイル: `~/.local/bin/`
- `stack setup`でインストールしたGHC 7.10.2の実行ファイル: `~/.stack/programs/x86_64-linux/ghc-7.10.2/bin/`

必要に応じてこれらも`PATH`に追加しちゃいましょう。

# 気が向いたら調べて記事にする

- [x] プロジェクトディレクトリーを作らないでとりあえずGHC一式だけ欲しい時の方法。
    - 何にもないディレクトリーで`stack setup`するだけでとりあえずインストールされるっぽい。`PATH`の設定はお好みで。
- [x] デフォルトのテンプレートに入っているユーザー名やメールアドレスを変える方法。
    - [書きました。](http://qiita.com/igrep/items/42130f58fb18da55e8c3)