はじめに
この記事は「Haskell (その4) Advent Calendar 2017」の13日目の記事になりました。
書籍「すごいHaskellたのしく学ぼう」は2012年に発売され、現在も販売されているHaskell入門にぴったりの良書です。しかし、内容が2012年当時のままで止まってしまっています。
そこでこの記事では(もう年末ですが)2017年現在「すごいHaskellたのしく学ぼう」を読むにあたって注意すべき点を章ごとにまとめて紹介します。
イントロダクション
イントロダクションの「Haskellの世界に飛び込むのに必要なもの」ではHaskell Platform
をインストールするように紹介されています。しかし、現在ではStackを利用するのが一般的です。
Linux系OSではリポジトリにある「stack」や「haskell-stack」などのパッケージを導入することで使えるようになりますし、もしリポジトリになくても以下のコマンドでインストールすることができます。ただし、Arch Linuxでは一部パッケージのビルドができない不具合があるため、AURにあるstack-static
パッケージをyaourtやpacaurなどで導入してください。
curl -sSL https://get.haskellstack.org/ | sh
macOSでは、Homebrewにhaskell-stack
パッケージがあるのでインストールしましょう。Windowsでは専用のインストーラーが公式サイトからダウンロードできるので活用してください。
Stackをインストールしたら、GHCの準備をします。結構時間がかかるので、以下のコマンドを入力して終わるまで放置しましょう。自動的にStackageのLTSで指定されているGHCをインストールしてくれます。
$ stack setup
終わったら起動GHCiをしてみましょう。Stack経由でインストールしたので、先頭にstack
をつけます。ghc
コマンドやrunghc
コマンドも同様です。面倒であればお使いのシェルの設定ファイルにエイリアスを加えてください。その際にはオプションの解釈を後ろ側のコマンドに寄せるために、--
を追加しておくと良いでしょう。
$ stack ghci
alias ghci='stack ghci'
alias ghc='stack ghc --'
alias runghc='stack runghc --'
第1章
リスト入門
「1.3: リスト入門」の脚注で「GHCiの中で名前を定義するときはletキーワードを使ってください」とありますが、執筆時点のGHCiバージョン(8.0.2)では不要になっています。従って、該当のサンプルコードは以下のように書くことができます。以下変数の束縛にlet
を用いる場面は全て同様に省略できます。
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を利用しています。従って、パッケージをグローバルにインストールする際には以下のコマンドを利用します。
$ stack install パッケージ名
第7章
とくになし
第8章
Hello, World!
イントロダクション
でも紹介しましたが、GHCでコードをコンパイルする際には先頭にstack
をつけるか、事前にエイリアスを設定しておいてください。また、--make
オプションはGHC 7.8.2より不要になりました。(@igrep さん、情報ありがとうございます!)
$ stack ghc helloworld # aliasなしの場合
$ ghc helloworld # aliasありの場合
第9章
ランダム性
System.Random
パッケージはデフォルトでインストールされなくなりました。利用するためには、以下のコマンドで個別に導入する必要があります。パッケージ導入後ghci
を再起動して、System.Random
を読み込めば手順を再現できます。
$ 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
を読み込む必要はなくなりました。例えば以下のコードはそのまま通るようになります。
Prelude> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
Prelude> foldr (*) 1 [1,2,3]
6
第13章
Monad型クラス
Monad型クラスはApplicative型クラス制約を付与して定義されるように変更されています。具体的には、以下の実装になっています。
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環境なので下記コマンドで代用します。
$ stack exec ghc-pkg -- list
また、mtlパッケージもデフォルトでは導入されていないため、インストールしておきましょう。
$ stack install mtl
モナドとしての関数
例によって(->) r
のモナドとしての定義はControl.Monad.Instances
ではなくGHC.Base
にあります。また、MonadがApplicativeになったおかげで、return
とpure
が等価な関数であると明確に定義されたため、Monadとしての定義にreturnを定義する必要がなくなりました。
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
に写し取るだけです。
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に挑むもよろしく!)
おわりに
色々見てきましたが、環境構築方法の変更や、MonadがApplicativeになった影響が一番大きかったかなと思います。ただ、5年経ってもこの本で紹介されているHaskellのコアな部分は変わりませんので、安心して学んでください!
-
正確には
Monomorphism Restriction
がデフォルトで無効化されました ↩