Cats と Scalaz の型クラスの違いメモ

Scalaz を使うことになったので、そこそこ使いなれた Cats との違いを、特に型クラスに着目して比べてみる。

## はじめに
自分が本格的に Scala を始めたときすでに Cats が普及し始めていて、仕事でも Cats から入ったわりと新参なので、Scalaz 自体はほとんど触ったことがない。ただ『FP in Scala』辺りは繰り返し読んでいるし、いろいろ知見の詰まった歴史のある老舗ライブラリということは知っているので、この際、ある程度使えるようにしておきたい。

そこで、この記事では Cats を起点にして違いをみてみる。Cats の ○○ が Scalaz ではどうなるか？という方向で観察するので、ベテラン Scala プログラマには順序が逆に思えるかもしれない。

## 方針
下図のクラス群を対象とする。
![cats_scalaz_typeclasses_diff2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9633/f68f250b-1d94-3029-8f7c-31151f3e2637.png)

[Cats サイトの型クラス図](https://typelevel.org/cats/typeclasses.html#type-classes-in-cats)の、`Functor` や `Monad` を含む core の階層をベースにして、 『[FP for Mortals](https://leanpub.com/fpmortals)』 に掲載されている [Scalaz の型クラス階層](https://leanpub.com/fpmortals/read#leanpub-auto-agenda)と対比してみた。ただし、、、

- Cats 側の `Unordered*` と `Commutative*` は省略した。
- 現状のソースとあっていない部分は、図を修正した。
- Cats は `1.6.0`、Scalaz は `7.2.27` を参考にした。

## 各型クラスのメモ
### 函手と○変
- **Invariant**: Scalaz では `InvariantFunctor`。メソッドも `imap` が `xmap` になる。
- **Functor**: だいたい同じ。
- **Covariant**: Invariant から継承した imap を除いてだいたい同じ。

### Foldable 系
- **Foldable**: だいたい同じ。
- **Traverse**: だいたい同じ。
- **Reducible**: Scalaz では `Foldable1`で、メソッドも `reduce` → `fold1` のように名称が変わる。[^foldable1]
- **NonEmptyTraverse**: Scalaz では `Traverse1`。メソッドも `nonEmptyTraverse` → `traverse1` となる。
- **Distributive**: `Traverse` の 双対の `Distributive` はどちらも同様に定義されている。

[^foldable1]: '1'は「少なくとも一つ」という意味での 1 だろうか？ 

### Apply 〜 Monad
- **Apply**: `ap`、`ap2` は同じだが、`map2` が Scalaz では `apply2` になる。また Cats では `InvariantSemigroupal` を継承しているが、Scalaz はそうではないという違いがあり、Cats の `product`（Semigroupal から継承）と、Scalaz の `product` (Apply[F], Apply[G] から Apply[(F, G)]への合成) では別物になる。
- **Applicative**: `pure` は共通だが、`Apply` と同様に Cats のみが `InvariantMonoidal` を継承しており、Cats 版では `InvariantMonoidal` から継承する `point` メソッドが、Scalaz では `Applicative` で初めて宣言される（`pure(a)=point(a)`）。
- **FlatMap**: Scalaz では `Bind` と呼ばれ、メソッド名も `flatMap` → `bind`、`flatten` → `join` と変わる。また**地味に大きな違い**として、Cats では `FlatMap` で宣言されている`tailRecM` が、Scalaz では別途 `BindRec` で宣言される。
- **Monad**: ほとんど同じ

### Semigroupal 〜 ContravariantMonoidal
以下は Cats にのみあって、Scalaz にはない。

- **Semigroupal**: (F[A], F[B])=>(F[(A, B)]) となるメソッド `product`が宣言される。Scalaz の `product` とは別物（上述）。
- **InvariantSemigroupal**: Cats でのみ `Apply` の基底型クラスとなる(上述)。
- **ContravariantSemigroupal**: (特になし)
- **InvariantMonoidal**: Cats でのみ `Applicative` の基底型クラスとなる(上述)。
- **ContravariantMonoidal**: (特になし)

### SemigroupK 〜 Alternative
- **SemigroupK**: Scalaz では `Plus` と呼ばれる。メソッドも `combineK` が `plus` になる。ただしエイリアスとなる演算子 `<+>` は共通。 
- **MonoidK**: 型名は Scalaz で `PlusEmpty` となり異なるが、メソッドは `empty` で同じ。
- **Alternative**: Scalaz では `ApplicativePlus` という型名になる。ソースを見比べると全然ちがうように見えるが、結局 `ap`、`pure`、`empty`、`combineK/plus` を継承する点でだいたい同じになる。
- **IsEmpty**: Scalaz のみで、`Option` のインスタンスなどが提供されている。
- **MonadPlus**: Scalaz のみの型クラス。Cats では `Alternative` で定義されている、`separate` や `unite` などが、Scalaz ではこちらで定義されている。

### Error系
- **ApplicativeError**: Cats のみ。Scalaz では `MonadError`で宣言されている `raiseError`、`handleError` などが、Cats では一階層上のこちらで宣言されている。
- **MonadError**: 上述のとおり、Scalaz ではこちらで`raiseError`、`handleError` などが宣言されている。Cats では `Monad`（したがって `FlatMap`も）を継承していることを利用した `ensure` や `rethrow` と言ったメソッドが定義されている。

### CoflatMap 〜 Bimonad
- **CoflatMap**: FlatMap/Bind と同様、Scalaz では `Cobind` というクラス名になり、メソッド名も同様に変わる。
- **Comonad**: 型クラス名は共通しているが、`extract` メソッドが Scalaz では `copoint` となる。
- **Bimonad**: Cats のみ。

### その他 Scalaz のみの型クラス
- **Divide**: `Apply` の *contravariant* な型クラスで、`apply2` に渡す関数の向きを逆にした `divide` メソッドが提供される。
- **Divisible**: `Applicative`の *contravariant* で、`pure` をひっくり返した `conquer` が提供される。
- **Align**: 「片側一方または両方」となるようなデータの組への操作を提供するもので、`\&/` (Cats での `Ior`) にも関連する。align*、merge、pad* などのメソッドが提供されていて、『FP for Mortals』では Map を題材にした具体例が紹介されている。
 
### ここで触れなかった型クラス
- 階層を成さない独立した型クラス群： Zip, Unzip, Cozip, Show, Optional など
- 代数系: Semigroup, Monoid, Band など
- 比較など: Equal, Order, Enum など
- モナドトランスフォーマー, Free/FreeT
- Coyoneda, Lan/Ran などレベル高めの圏論型クラス

追々調べる。

## おわりに
- Cats より記号が多めな気がするが、記号嫌いじゃない派なので好ましい。`\/` はよく見るけど、`Ior` 相当のものが `\&/` だったり、オブジェクト指向の文脈でもよくみるリスコフの置換原理が、`<~<` とか型で表現されてるのも興味深い。
- 型クラス以外にも便利そうな小物が多い。例えば `Memo` とか `Endo` とか、ほぼ同じコードを自前で書いたりしていた。こういうのをいろいろ発見して、また Cats プロジェクトやるときにも知見を活かしたい。
- 今回は型クラスのみ調べたが、[データ型](https://leanpub.com/fpmortals/read#leanpub-auto-scalaz-data-types)も Cats に無いのが多くて面白そう。
- 思ったより相違点が多いが、逆に勉強になる。