Haskellの基本文法を学んだあとに読む記事

## はじめに

この記事では、Haskellの基本文法を身につけた皆さんに向けて、これから実用的なHaskellアプリを開発したり、既存のHaskellコードを読むにあたって役に立つ事柄を紹介します。
またこの記事を読むと、この記事で紹介していないものについても、混乱せずに自分で調べて学んでいけるように（たぶん）なります。

必ずしも頭から読む必要はなく、知りたいと思った節から読んでいくのが良いかと思います。

目安として、[ウォークスルー Haskell](http://walk.northcol.org/haskell/)を全て学んでいる相当の知識を前提とします。

### 目次

* Readerモナド, Writerモナド, Stateモナドの簡単な解説
    * これらはIO, Maybe, List以外で特に重要なモナドです。
    * モナドを合成する手段であるモナドトランスフォーマーの説明もします。
* Haskellの文字列事情の解説記事へのリンク
* Haskellの例外事情の解説記事へのリンク
* Haskellの基本的なイディオムの紹介
    * Lens, Hasパターン
* 特に重要なGHC拡張の紹介
    * TupleSections, NamedFieldPuns, MultiParamTypeClasses
    * その他の重要なGHC拡張へのリンク
* Haskellerがよく使う周辺ツールの紹介
    * ウェブサイト、環境構築ツール、Lintなど

## mtl

mtlとは、いくつかの基本的なモナドを提供するライブラリです。

Haskellでよく利用するモナドは実はそう多くありません。
標準ライブラリで提供される`IO`モナド、`Maybe`モナド、リストモナドに加えて、mtlが提供する`Reader`モナド、`Writer`モナド、`State`モナドの使い方が分かれば、後はこれらを組み合わせて自作モナドを作っていくことが可能です。

https://hackage.haskell.org/package/mtl

`IO`モナド、`Maybe`モナド、リストモナドについてはご存知かと思うので、mtlが提供する基本的なモナド3種について簡単に紹介します。

### Readerモナド

イミュータブルな環境`r`を持ち、いつでも文脈から`r`を読み出すことができるモナドです。

定義は次の通りです。関数に`Reader`と名前をつけただけの、シンプルな構造です。

```Haskell
newtype Reader r a = Reader
    { runReader :: r -> a
    }

ask :: Reader r r
ask = Reader id
```

Readerモナドの文脈中では、`ask`アクションを用いて好きに環境`r`を取ってくることができます。
しばしば利用される例として、プログラム全体をReaderモナドでラップして、コマンドライン引数やアプリケーションの設定などを利用できるようにすることが挙げられます。

コマンドライン引数などから`Env`を作成して、メインの処理は全て`ReaderT`の文脈で実行する例です。
プログラム内部では、`ask`を利用して`verbose`の設定値を読み取り、それによってデバッグログを出力するかどうかを判断しています。

```Haskell
main :: IO ()
main = do
    let env = Env {envVerbose = True}
    flip runReaderT env $ do
        verbose <- envVerbose <$> ask
        when verbose $
            lift . print $ "DEBUG: Hello World!"
        lift . print $ "Your application is running!"
```

なお、`ReaderT`と末尾に`T`がついているのは、後述するモナドトランスフォーマーと呼ばれる変種を使っているからです。

コード全文はこちらです。

https://wandbox.org/permlink/z6FlvsWMzfjUC7g8

### Writerモナド

Writerモナドは、文脈中で[モノイド](https://hackage.haskell.org/package/base-4.18.0.0/docs/Prelude.html#t:Monoid)を書き出していくことができるモナドです。

定義は次の通りです。ペアに`Writer`と名前をつけただけの、シンプルな構造です。

```Haskell
newtype Writer w a = Writer
    { runWriter :: (a, w)
    }

tell :: w -> Writer w ()
tell w = Writer ((), w)
```

Writerモナドの文脈では、`tell`アクションを用いてモノイド`w`を書き出すことができます。

よくあるユースケースとしては、たくさんのモノイドを手続き的な書き方で結合させたい場合があります。

```Haskell
let (_, html) = runWriter $ do
        tell "<html>"
        tell   "<p>Hello World</p>"
        tell "</html>"
```

これは次のコードと同じ意味です。

```Haskell
let html2 = "<html>"
        <> "<p>Hello World</p>"
        <> "</html>"
```

あるいは、`Data.Foldable`の`fold`関数を使って、次のように書くこともあります。

```Haskell
let html3 = fold
        [ "<html>"
        , "<p>Hello World</p>"
        , "</html>"
        ]
```

モノイドは様々な場所で使われるため、場合に合わせて最も読みやすくなる書き方を選択します。

コード全文はこちらです。

https://wandbox.org/permlink/spxknblxFiZ0vEVa

Writerモナドについての注意点として、素直な実装のWriterモナドにはスペースリークの問題があるため、下に貼った私の実装を含め、独自実装は使わないようにしてください。
mtlに含まれるWriterモナドはこの問題を回避するため、安心して使うことができます。

https://qiita.com/ruicc/items/28b1cd2df215ce724e26

### Stateモナド

文脈中でミュータブルな状態`s`を扱うことができるモナドです。

定義は次の通りです。関数に`State`と名前をつけただけの構造です。

```Haskell
newtype State s a = State
    { runState :: s -> (a, s)
    }

get :: State s s
get = State $ \s -> (s, s)

put :: s -> State s ()
put s = State $ const ((), s)

modify :: (s -> s) -> State s ()
modify f = get >>= put . f
```

Readerよりもやや複雑な関数に見えますが、「状態を受け取って、計算結果と新しい状態を返す」ような関数がStateモナドとみなせる、ということです。

Stateのアクションは3つあり、現在の状態を取得する`get`、新しい状態を設定する`put`、現在の状態に変更を加える`modify`があります。
これにより、定数しか使えないHaskellの中で疑似的に変数を扱うことができるようになります。

Stateモナドのユースケースとしては、自作モナドのプリミティブとして使うことが多いと思います。

自作モナドについては、続くモナドトランスフォーマーで説明します。

### モナドトランスフォーマー

モナドトランスフォーマーとは、モナドを合成可能にする仕組みです。

Readerモナドの例で出てきたコードは、まさにモナドトランスフォーマーを使った例です。
この例では、`Reader`と`IO`を合成して、環境を持つReaderの特性と標準出力が可能なIOの特性を両立するモナドを作成しています。

```Haskell
main :: IO ()
main = do
    let env = Env {envVerbose = True}
    flip runReaderT env $ do
        verbose <- envVerbose <$> ask
        when verbose $
            lift . print $ "DEBUG: Hello World!"
        lift . print $ "Your application is running!"
```

モナドトランスフォーマーは、各モナドごとに作成します。
例えば`ReaderT`は次のように実装されています。

```Haskell
newtype ReaderT r m a = ReaderT
    { runReaderT :: r -> m a
    }

ask :: Monad m => ReaderT m r r
ask = ReaderT pure
```

Readerの定義と見比べると型パラメータに`m`が増えており、これが合成対象のモナドとなっています。
Readerモナドの使用例と称して使ったモナドの型は実際には`ReaderT Env IO`であり、これがReaderモナドとIOモナドを合成したモナドということになります。

また、モナドトランスフォーマーは一般に`MonadTrans`という型クラスを実装します。

```Haskell
class MonadTrans t where
    lift :: Monad m => m a -> t m a
```

モナドトランスフォーマーによるモナドの合成には、上下関係があります。
例えば、`ReaderT r IO`というモナドは、IOの上にReaderTを被せています。この上に`Writer`モナドを被せれば、`WriterT w (ReaderT r IO)`となります。これをモナドスタックと呼びます。
`lift`は、下位のモナドのアクションを一つ上のモナドのアクションに持ち上げます。

例えば`ReaderT Env IO a`モナドの中で`print`などの`IO`のアクションを実行したい場合、`lift . print`とすることで戻り値の`IO ()`を`ReaderT Env IO ()`に変換します。

モナドを自作するというと、ほとんどの場合、モナドトランスフォーマーを使って必要な性質を組み合わせて一つのモナドを合成することを言います。

次は、典型的な自作モナドの例です。

```Haskell
newtype MyApp a = MyApp (ReaderT MyEnv (StateT MyState IO) a)
```

#### モナドトランスフォーマーの問題点とExtensible Effects

モナドトランスフォーマーにはいくつかの問題点が指摘されています。

1. 速度が遅い
2. `lift`が面倒くさい
3. 合成の順序によってモナドの特性が変わる
4. 合成の順序を入れ替えることができない

これを克服する代替のモナドの合成手段として、Extensible Effectsが存在します。
Extensible Effectsについては発展的な内容になるため、必要になったら調べてみてください。

モナドトランスフォーマーの問題点とExtensible Effectsについて、より詳しくは次の記事が参考になります。

https://konn-san.com/prog/haskell/extensible-effects.html

## 文字列ライブラリ

残念なことに、Haskellの文字列事情は、非常に込み入っています。
ここで解説するにはあまりにも複雑なので、いくつかの記事を紹介します。

https://qiita.com/mod_poppo/items/740659702f31216fdade

https://scrapbox.io/haskell-shoen/%E6%96%87%E5%AD%97%E5%88%97

基本的には[text](https://hackage.haskell.org/package/text)パッケージを使い、必要に応じて[bytestring](https://hackage.haskell.org/package/bytestring)パッケージを使います。
トイプログラム以外では標準の`String`は使わないのが無難です。

https://hackage.haskell.org/package/text

https://hackage.haskell.org/package/bytestring

## 例外

ウォークスルーHaskellには出てきませんが、Haskellにも例外という概念はあります。
`Maybe`モナドや`Either`モナド、`List`モナドには例外処理という側面がありますし、例えば非網羅的なパターンマッチやIO処理の失敗から送出される例外もあります。

しかし、Haskellは例外事情も込み入っており、例外処理に標準ライブラリではなく代替のライブラリを使うことが推奨されています。
例外事情については以下の記事が参考になります。

https://qiita.com/rinse_/items/596cc07242aef880cf81

## イディオム

### Lens

Haskellの貧弱なレコードを補うイディオムです。
次の`Lens`という型を用いると、HaskellでGetterやSetterをオブジェクト指向の言語のように扱うことができます。

```Haskell
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
type Lens' s a = Lens s s a a
```

使い心地は次のような感じです。

```Haskell
let p = Person{name = "John", age = 20}
print $ "name : " <> p^.nameL
print $ "age : " <> show (p^.ageL)

let q = p & nameL .~ "Jeanne"
print $ q
```

コード全体は次に置いてあります。

https://wandbox.org/permlink/lpjmp9SFpZLJBUKq

この通りシンプルなイディオムではあるのですが、様々なバリアントと膨大な取り巻きを擁する巨大なライブラリが用意されています。

https://hackage.haskell.org/package/lens

実用上は軽量なmicrolensパッケージを使うのがおすすめです。

https://hackage.haskell.org/package/microlens

### Hasパターン

Haskellの貧弱なレコードを、Lensと型クラスを使って補うイディオムです。
私が書いた記事で恐縮ですが、以下にまとめています。

https://qiita.com/sparklingbaby/items/b6c0e87c0299286e5e17

## GHC拡張

Haskellに新しい文法を導入したり、既存の文法を変更したりする、コンパイラ独自の拡張です。
Haskell関連の記事や既存のコードを読んでいて、全く知らない文法が登場したら、恐らくそれはGHC拡張です。

GHC独自の拡張ではあるのですが、GHCがデファクトスタンダードであることと、もはやなくてはならない機能が多数含まれるために、学ぶことが必須となっています。恐らくですが、世界中のHaskellコードのうちGHC拡張を一切使っていないコードは1%も存在しないと思います。

ここでは、主要なGHC拡張のうち特に重要なもののみを紹介します。

### TupleSections

Haskellでは、[セクション](http://walk.northcol.org/haskell/operators/#_%E3%82%BB%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)という記法を用いることで、二項演算子に対しても部分適用を利用することができます。

[TupleSections](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/tuple_sections.html)を用いると、このセクションをタプルに対しても利用することができるようになります。

```Haskell
(, True) -- ==> \x -> (x, True)
```

https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/tuple_sections.html#extension-TupleSections

### NamedFieldPuns

フィールドを変数に束縛するとき、しばしば次のようにフィールド名と同名の変数を宣言することがあります。

```Haskell
getName :: Person -> String
getName Person {name=name} = name
```

NamedFieldPunsを使うと、これを次のように書けます。

```Haskell
getName :: Person -> String
getName Person {name} = name
```

また値の分解時だけでなく、構築時にもNamedFieldPunsを使えます。
フィールドの多いレコードを扱うときには非常に便利な拡張です。

```Haskell
let name = "John"
 in Person {name}
```

https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/record_puns.html#extension-NamedFieldPuns

### MultiParamTypeClasses

ここまではただ便利な構文を紹介してきましたが、拡張機能がなければ実現できない拡張をいくつか紹介します。
MultiParamTypeClassesは、複数のパラメータを取る型クラスを書けるようにする拡張です。

型クラスは通常、ある型についての特性を記述します。
例えば`Show`を実装する型`a`は`show`メソッドを通じて文字列にすることができます。
複数のパラメータを取る型クラスは、複数の型の間の関係を記述します。

次の`Iso`型クラスは、型`a`と型`b`が同型であることを表しています。（同型は数学の用語ですが、直感的には「ほぼ同じ」という意味です）

```Haskell
class Iso a b where
    iso  :: a -> b
    iso' :: b -> a
```

この型クラスを実装でき、かつ条件`iso . iso' == id`と`iso' . iso == id`を満たす場合、型`a`と型`b`は同型であるといえます。

* 例えば、`(String, Int)`と`(Int, String)`は同型であることは自明です。
* `Int`と`String`は同型ではなさそうです。
    * `Int -> String`は実装できそうですが、逆の変換は`String -> Maybe Int`となってしまい、実装できなそうです。
* `a -> b -> c`と`(a, b) -> c`同型です。
    * `Iso`のインスタンスを書くことはできるでしょうか？
    * `Iso`のインスタンスを書くには[FlexibleInstances](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/instances.html#extension-FlexibleInstances)拡張を有効化する必要があることに注意してください。
    * MultiParamTypeClassesを制約として書くには[FlexibleContexts](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/flexible_contexts.html#extension-FlexibleContexts)拡張を有効化する必要があることに注意してください。

もう少し実用的な例として、*mtl*の`MonadReader`型クラスを見てみましょう。

```Haskell
class Monad m => MonadReader r m | m -> r where
    ask :: m r
```

`MonadReader`型クラスは、`Reader`モナドを型クラスに一般化した型クラスではあるのですが、字面に注目すれば環境`r`の型とモナドであるような型`m`の関係を表しています。
なお、`| m -> r`の部分は[FunctionalDependencies](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/functional_dependencies.html#extension-FunctionalDependencies)といい、`m`が決まれば`r`は一意に定まることを意味しています。

https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/multi_param_type_classes.html#multi-parameter-type-classes

### その他の拡張

主要なGHC拡張は実際のところまだまだあります。
不自由なくHaskellを書こうと思ったら、少なくとも以下のGHC拡張は必須だと思います。（これでも厳選しています）

* [MultiParamTypeClasses](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/multi_param_type_classes.html#multi-parameter-type-classes)に付随して必要となる拡張群
    * [FlexibleInstances](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/instances.html#extension-FlexibleInstances)
    * [FlexibleContexts](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/flexible_contexts.html#extension-FlexibleContexts)
    * [FunctionalDependencies](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/functional_dependencies.html#extension-FunctionalDependencies)
* [OverloadedStrings](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/overloaded_strings.html#extension-OverloadedStrings)
    * ダブルクォートで囲った文字列を`String`以外の型にも推論できるようにします。
    * Haskellの貧弱な文字列を扱う上で必須です。
* [GeneralisedNewtypeDeriving](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/newtype_deriving.html#extension-GeneralisedNewtypeDeriving)
    * `newtype`で新たな型を作成するとき、元の型が持っていたインスタンスを`deriving`キーワードを使って実装できるようになります。
* [GADTs](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/gadt.html#extension-GADTs)
    * 一般化された代数的データ型を定義できるようになります。

もし他に使うべきGHC拡張が知りたい場合、rioというライブラリがデフォルトで有効化するべきと主張するGHC拡張のリストが参考になります。（ライブラリを使う必要はありません）

https://hackage.haskell.org/package/rio#language-extensions

GHC拡張について、より詳しくはGHCのドキュメントを参照してください。

https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/table.html

## 周辺ツール

### GHCup

GHCやStack, Cabal, Haskell Language Serverなどの、Haskell開発の周辺ツールのバージョンを管理ができるツールです。

https://www.haskell.org/ghcup/

* StackやCabalは、Haskellプロジェクトを管理するためのツールです。
    * 依存関係やプロジェクト構成などを管理できます。
    * 一時期はStackが有力でしたが、最近再びCabalが盛り返しつつあります。
* Haskell Language Server (HLS) は、IDEがソースを解析を行うための諸情報を提供します。
    * これを導入することで、VSCodeなどお好みのエディタをIDEにすることができます。
    * HIE (Haskell IDE Engine) は古いのでお勧めしません。

もしHaskellの開発環境をセットアップしていない場合は、こちらのブログが参考になります。

https://zenn.dev/mod_poppo/articles/haskell-setup-2023

### Hoogle

Hoogleは、定数名や関数名、関数のシグネチャからドキュメントをライブラリ横断的に検索することができるサイトです。

https://hoogle.haskell.org/

特にググラビリティの低い演算子の検索に重宝すると思います。

試しに`>>=`演算子を検索してみましょう。
この演算子のシグネチャがどんなシグネチャをしていて、どの型クラスのメソッドで、どのパッケージに含まれていて、どのライブラリに含まれているか、ということが分かります。

https://hoogle.haskell.org/?hoogle=%3E%3E%3D

### Wandbox

小さいコード片をさっと実行したり、Haskellコードを共有してみたりするのに便利です。

https://wandbox.org/

もし使い慣れたオンラインコンパイラがHaskellに対応していれば、それでも全然問題ないと思います。

### Haddock

Haskell用のドキュメント生成ツールです。
JavaDocやJSDocのように、専用の記法を使うことでソースコードからドキュメントを生成できます。

いつもお世話になっている[Hackage](https://hackage.haskell.org/)上のドキュメントは、このHaddockを使って生成されています。

https://haskell-haddock.readthedocs.io/en/latest/

### Stylish HaskellとHLint

[Stylish Haskell](https://github.com/haskell/stylish-haskell)はコードの自動整形ツールです。
新しい言語を始めたばかりの頃は特に、その言語での文化圏ではどのようなコードスタイルが一般的なのかが分からないと思います。
他の言語に漏れず、Haskellの文化圏でもコードスタイルは人によってまちまちではあるのですが、Stylish Haskellに従っておけばとりあえずは間違いありません。

[HLint](https://github.com/ndmitchell/hlint)はHaskell向けのLintです。
他のLintと同じようにコードの問題点を解析してくれるのですが、使い道が初見では分からないような関数が多いHaskellでは、初心者とって極めて有用です。
まるで先輩にコードレビューしてもらっているように、標準ライブラリに存在する関数を新たに知ることができたり、標準ライブラリにある関数の使い方を初めて理解できたりするケースがあります。

Stylish HaskellやHLintについて知るには、次の記事もおすすめです。

https://www.ncaq.net/2017/10/07/

## ブログなど

### Haskell-jp

Haskellの日本語話者のユーザーグループです。

Haskellの情報を発信したり、勉強会やイベントの開催をしています。
Slackチームに参加すれば、分からないことを質問したり、共にHaskellを学ぶ人を見つけることができるかもしれません。

https://haskell.jp/

### Haskell荘園

Haskellに関する攻略情報が共有されています。
頻出パターンやベストプラクティス、Haskellの問題点などが雑多に載っています。

https://scrapbox.io/haskell-shoen/

## 用語

Haskellをやっているとよく出てくるが、他のプログラミング言語にはあまり出てこない用語。

* 文脈
    * （特にdo文を使って）モナディックな値を生成する過程を、モナドの文脈などという。
    * 文脈ではそのモナドのアクション（後述）を使うことができる。
    ```Haskell
    main :: IO ()
    main = do
        -- ここはIOモナドの文脈にある
        putStrLn "Hello World" -- IO () という値を生成する

        flip runReaderT "Bonjour Monde" $ do
            -- ここは ReaderT Env IO モナドの文脈にある
            greeting <- ask
            lift $ putStrLn greeting -- ReaderT String IO () という値を生成する
    ```
* アクション
    * モナディックな値、またはモナディックな値を返す関数のことを指します。
    * 例えば、`putStrLn`は`String -> IO ()`であるため「IOのアクションを返す関数」あるいは単に「IOのアクション」などと言ったりします。
    * しばしば型クラスに括り出されて一般化されます。
        * 単純にアクションを型クラスに括り出すだけでも、アクションをモックモナドの上で動かすことができるようになったりします。
    * アクションについてはこちらの記事も参考になります。
        * https://haskell.jp/blog/posts/2020/io-monad-and-sideeffect.html