2017年に「すごいHaskellたのしく学ぼう」を読む

# はじめに
この記事は「[Haskell (その4) Advent Calendar 2017](https://qiita.com/advent-calendar/2017/haskell4)」の13日目の記事になりました。

書籍「すごいHaskellたのしく学ぼう」は2012年に発売され、現在も販売されているHaskell入門にぴったりの良書です。しかし、内容が2012年当時のままで止まってしまっています。

そこでこの記事では(もう年末ですが)2017年現在「すごいHaskellたのしく学ぼう」を読むにあたって注意すべき点を章ごとにまとめて紹介します。

# イントロダクション
イントロダクションの「Haskellの世界に飛び込むのに必要なもの」では`Haskell Platform`をインストールするように紹介されています。しかし、現在では[Stack](https://docs.haskellstack.org/en/stable/README/)を利用するのが一般的です。

Linux系OSではリポジトリにある「stack」や「haskell-stack」などのパッケージを導入することで使えるようになりますし、もしリポジトリになくても以下のコマンドでインストールすることができます。ただし、Arch Linuxでは一部パッケージのビルドができない不具合があるため、AURにある`stack-static`パッケージをyaourtやpacaurなどで導入してください。

```bash
curl -sSL https://get.haskellstack.org/ | sh
```

macOSでは、Homebrewに`haskell-stack`パッケージがあるのでインストールしましょう。Windowsでは専用のインストーラーが[公式サイト](https://docs.haskellstack.org/en/stable/README/)からダウンロードできるので活用してください。

Stackをインストールしたら、GHCの準備をします。結構時間がかかるので、以下のコマンドを入力して終わるまで放置しましょう。自動的に[StackageのLTS](https://www.stackage.org/lts)で指定されているGHCをインストールしてくれます。

```bash
$ stack setup
```

終わったら起動GHCiをしてみましょう。Stack経由でインストールしたので、先頭に`stack`をつけます。`ghc`コマンドや`runghc`コマンドも同様です。面倒であればお使いのシェルの設定ファイルにエイリアスを加えてください。その際にはオプションの解釈を後ろ側のコマンドに寄せるために、`--`を追加しておくと良いでしょう。

```bash
$ stack ghci
```

```bash:.bashrc
alias ghci='stack ghci'
alias ghc='stack ghc --'
alias runghc='stack runghc --'
```

# 第1章
## リスト入門
「1.3: リスト入門」の脚注で「GHCiの中で名前を定義するときはletキーワードを使ってください」とありますが、執筆時点のGHCiバージョン(8.0.2)では不要になっています。従って、該当のサンプルコードは以下のように書くことができます。以下変数の束縛に`let`を用いる場面は全て同様に省略できます。

```haskell
ghci> lostNumbers = [4,8,15,16,23,42]
ghci> lostNumbers
[4,8,15,16,23,42]
```

# 第2章
とくになし

# 第3章
とくになし

# 第4章
とくになし

# 第5章
とくになし

# 第6章
## モジュール
訳注に以下の記述があります。

> Haskell Platformをインストールしてあるなら、cabalコマンドが使えるはず。「`cabal install パッケージ名`」というコマンド一発で、様々なパッケージをインストールできますよ！

しかし今回はHaskell Platformではなくstackを利用しています。従って、パッケージをグローバルにインストールする際には以下のコマンドを利用します。

```bash
$ stack install パッケージ名
```

# 第7章
とくになし

# 第8章
## Hello, World!
`イントロダクション`でも紹介しましたが、GHCでコードをコンパイルする際には先頭に`stack`をつけるか、事前にエイリアスを設定しておいてください。また、`--make`オプションはGHC 7.8.2より不要になりました。(@igrep さん、情報ありがとうございます！)

```bash
$ stack ghc helloworld # aliasなしの場合
$ ghc helloworld       # aliasありの場合
```

# 第9章
## ランダム性
`System.Random`パッケージはデフォルトでインストールされなくなりました。利用するためには、以下のコマンドで個別に導入する必要があります。パッケージ導入後`ghci`を再起動して、`System.Random`を読み込めば手順を再現できます。

```bash
$ stack install random
```

# 第10章
とくになし

# 第11章
## ファンクターとしての関数
関数がファンクターとして定義されているモジュールが`Control.Monad.Instances`であると紹介されていますが、すでに`GHC.Base`に移されています。`Control.Monad.Instances`は将来的に削除予定なので気をつけましょう。

また、`NoMonomorphismRestriction`オプションを有効化するように指示されていますが、GHC 7.8.1からデフォルトで有効化[^1]されるようになったのであえて付け加える必要はありません。

## アプリカティブスタイル
> Control.Applicativeはfmapと等価な中値演算子<$>をエクスポートしています。

とありますが、`<$>`は特に何もインポートしなくても使えるようになっています。`<*>`や`pure`も同様です。

# 第12章
## モノイドで畳み込む
Preludeに定義されている`foldl`などのFoldableな関数について、かつては`Foldable`型クラス制約が付いていませんでした。しかしGHC 7.10からはつくようになったので、わざわざ`Data.Foldable`を読み込む必要はなくなりました。例えば以下のコードはそのまま通るようになります。

```haskell
Prelude> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> foldr (*) 1 [1,2,3]
6
```

# 第13章
## Monad型クラス
Monad型クラスはApplicative型クラス制約を付与して定義されるように変更されています。具体的には、以下の実装になっています。

```haskell
class Applicative m => Monad m where
    (>>=)       :: forall a b. m a -> (a -> m b) -> m b
    (>>)        :: forall a b. m a -> m b -> m b
    m >> k = m >>= \_ -> k

    return      :: a -> m a
    return      = pure

    fail        :: String -> m a
    fail s      = errorWithoutStackTrace s
```

> Monadのインスタンスは必ずApplicativeのインスタンスでもあるよう、クラス宣言の前に、class (Applicative m) => Monad m whereという型クラス制約があるべきではないでしょうか

という記述がありますが、当時とは異なり現在ではそのような実装に変更されています。

# 第14章
## もうちょっとだけモナド
パッケージの一覧取得に`ghc-pkg list`コマンドを使うように紹介されていますが、例によってstack環境なので下記コマンドで代用します。

```bash
$ stack exec ghc-pkg -- list
```

また、mtlパッケージもデフォルトでは導入されていないため、インストールしておきましょう。

```bash
$ stack install mtl
```

## モナドとしての関数
例によって`(->) r`のモナドとしての定義は`Control.Monad.Instances`ではなく`GHC.Base`にあります。また、MonadがApplicativeになったおかげで、`return`と`pure`が等価な関数であると明確に定義されたため、Monadとしての定義にreturnを定義する必要がなくなりました。

```haskell
instance Applicative ((->) a) where
    pure = const
    (<*>) f g x = f x (g x)
    liftA2 q f g x = q (f x) (g x)

instance Monad ((->) r) where
    f >>= k = \ r -> k (f r) r
```

## Readerモナド
`addStuff`関数を定義しているサンプルコードでは`Control.Monad.Instances`を読み込んでいますが、上記理由により不要になっています。

## liftMと愉快な仲間たち
> MonadにもApplicativeに対して同じような型クラス制約が付いているべきだった

とありますが、先ほど説明したとおりすでに付与されています。

## モナドを作る
MonadがApplicativeになった影響で、Monadだけ実装してもコンパイルが通らなくなってしまいました。とりあえずコンパイルを通すために、Applicativeの実装も付け加えましょう。実装は簡単で、`return`と`pure`が等しいことを利用して、`return`の内容を`pure`に写し取るだけです。

```haskell
import Data.Ratio

newtype Prob a = Prob
    { getProb :: [(a, Rational)]
    } deriving Show

instance Functor Prob where
    fmap f (Prob xs) = Prob $ map (\(x, p) -> (f x, p)) xs

flatten :: Prob (Prob a) -> Prob a
flatten (Prob xs) = Prob $ concat $ map multAll xs
    where multAll (Prob innerxs, p) = map (\(x, r) -> (x, p * r)) innerxs

instance Applicative Prob where
    pure x = Prob [(x, 1 % 1)]

instance Monad Prob where
    m >>= f = flatten (fmap f m)
    fail _ = Prob []
```

# 第15章
とくになし ([Zipperに挑む](https://qiita.com/Aruneko/items/dee20161358e7c39e27a)もよろしく！)

# おわりに
色々見てきましたが、環境構築方法の変更や、MonadがApplicativeになった影響が一番大きかったかなと思います。ただ、5年経ってもこの本で紹介されているHaskellのコアな部分は変わりませんので、安心して学んでください！

[^1]: 正確には`Monomorphism Restriction`がデフォルトで無効化されました