Freer Effectsが、だいたいわかった: 11-2 TypeOperators拡張

Freer Effectsが、だいたいわかった: 11-2 TypeOperators拡張
=========================================================

目次
----

(0). [導入](https://qiita.com/YoshikuniJujo/items/c71644b5af1f5195cbf3)

1. [Freeモナドの概要](https://qiita.com/YoshikuniJujo/items/988ac4b69a27974154fd)
	* Freeモナドとは
	* FreeモナドでReaderモナド、Writerモナドを構成する
2. [存在型(ExistentialQuantification拡張)の解説](
	https://qiita.com/YoshikuniJujo/items/988ac4b69a27974154fd )
3. [型シノニム族(TypeFamilies拡張)の解説](https://qiita.com/YoshikuniJujo/items/17853e423c39409d8dfe)
4. [データ族(TypeFamilies拡張)の解説](https://qiita.com/YoshikuniJujo/items/b3c442d0387a35bfe860)
5. [一般化代数データ型(GADTs拡張)の解説](https://qiita.com/YoshikuniJujo/items/739a1e3abed8c602dba7)
6. [ランクN多相(RankNTypes拡張)の解説](https://qiita.com/YoshikuniJujo/items/4094b5fe5a7a33f3d1e6)
7. [FreeモナドとCoyoneda](https://qiita.com/YoshikuniJujo/items/729ad9830833eacb9d75)
	* Coyonedaを使ってみる
	* FreeモナドとCoyonedaを組み合わせる
		+ いろいろなモナドを構成する
8. [Freerモナド(Operationalモナド)でいろいろなモナドを構成する](
	https://qiita.com/YoshikuniJujo/items/686fedc92fd20ff70ab8 )
	* FreeモナドとCoyonedaをまとめて、Freerモナドとする
	* Readerモナド
	* Writerモナド
	* 状態モナド
	* エラーモナド
9. [モナドを混ぜ合わせる(閉じた型で)](
	https://qiita.com/YoshikuniJujo/items/19a6e9dada698a5ebfb6 )
	* Freerモナドで、状態モナドとエラーモナドを混ぜ合わせる
		+ 両方のモナドを一度に処理する
		+ それぞれのモナドを、それぞれに処理する
10. [存在型による拡張可能なデータ構造(Open Union)](
	https://qiita.com/YoshikuniJujo/items/8dd63c9415ccda20be28 )
11. 追加の言語拡張
	1. [ScopedTypeVariables拡張](
		https://qiita.com/YoshikuniJujo/items/103807ee6692e8c2c48b )
	2. TypeOperators拡張
	3. [KindSignatures拡張](https://qiita.com/YoshikuniJujo/items/0f581acc78d2ba8e3a7c)
	4. DataKinds拡張
	5. MultiParamTypeClasses拡張
	6. FlexibleInstances拡張
	7. OVERLAPSプラグマ
12. Open Unionを型によって安全にする
13. モナドを混ぜ合わせる(開いた型で)
	* FreeモナドとOpen Unionを組み合わせる
	* 状態モナドにエラーモナドを追加する
14. Freer Effectsで、IOモナドなどの、既存のモナドを使用する
15. 関数を保管しておくデータ構造による効率化
16. いろいろなEffect
	* 関数handleRelayなどを作成する
	* NonDetについて、など

直積と直和
----------

Haskellでは型を組み合わせることができる。組み合わせかたには「ふたつの型の両方の値をもつ型をつくる」、または、「ふたつの型の、どちらかの値をもつ型をつくる」というやりかたがある。それぞれ、「直積」「直和」のようによぶ。

### 直積

ふたつの型の両方の値をもつ値は、つぎのように表現できる。

```hs
> ('c', True)
('c',True)
> :type it
it :: (Char, Bool)
```

代数的データ型では、つぎのようになる。

```hs
> data Product a b = Product a b deriving Show
> Product 'c' True
Product 'c' True
> :type it
it :: Product Char Bool
```

これを直積とよぶ。値の「数」を考えると「積」であることが直観的によくわかる。TrueとFalseとの、ふたつの値をもつBool型と、A、B、Cという、みっつの値をもつT型との直積型は、つぎのようになる。

```hs
(Bool, T)
```

この型に属する値は、つぎのようになる。

```hs
(False, A)
(False, B)
(False, C)
(True, A)
(True, B)
(True, C)
```

このように2個の値をもつ型と、3個の値をもつ型との直積である型は、6個の値をもつ。「2かける3で6」だ。

### 直和

ふたつの型の、どちらかの型の値をもつ型の値は、つぎのように表現できる。

```hs
> Left 'c' :: Either Char Bool
Left 'c'
> :type it
Either Char Bool
```

代数的データ型では、つぎのようになる。

```hs
> data Sum a b = L a | R b deriving Show
> L 'c' :: Sum Char Bool
L 'c'
> :type it
it :: Sum Char Bool
```

これを直和とよぶ。値の「数」を考えてみよう。TrueとFalseの、ふたつの値をもつBool型と、A、B、Cという、みっつの値をもつT型との直和型は、つぎのようになる。

```hs
Either Bool T
```

この型に属する値は、つぎのようになる。

```hs
Left False
Left True
Right A
Right B
Right C
```
このように2個の値をもつ型と、3個の値をもつ型との直和である型は、5個の値をもつ。「2たす3で5」だ。

直積型と直和型を定義する
------------------------

タプルとEither型は、そのまま直積と直和である。また、代数的データ型も、そのままで直積と直和を表現できる。なので、ここで新たな「直積型」「直和型」を定義する理由はないが、説明の都合上、定義することとする。ファイルproductSum.hsを作成する。

```hs:productSum.hs
data Product a b = Product a b deriving Show
data Sum a b = L a | R b deriving Show

x :: Sum (Product Char Bool) (Product String Integer)
x = L $ Product 'c' True
```

試してみる。

```hs
> :load productSum.hs
> x
L (Product 'c' True)
> :type x
x :: Sum (Product Char Bool) (Product String Integer)
```

値構築演算子
------------

値構築演算子を使うと、直積型はより読みやすくなる。productSum.hsの、データ型Productの定義を修正する。

```hs:productSum.hs
data Product a b = a :*: b deriving Show
```

値構築演算子は、ふつうの演算子と名前空間がおなじなので、区別するために、その名前には:(コロン)から、はじまる識別子を使う。最後にも:(コロン)をつけたのは、そのほうが見た目のバランスがいいからで、とくに深い意味はない。サンプルの値xの定義も修正する。

```hs:productSum.hs
x = L $ 'c' :*: True
```

```hs
> :reload
> x
L ('c' :*: True)
> :type x
x :: Sum (Product Char Bool) (Product String Integer)
```

型構築演算子
------------

さて、関数にたいして演算子があり、値構築子にたいして値構築演算子がある。それでは、型構築子にたいして型構築演算子は使えるだろうか。使える。そう、GHCならね。つぎのようにファイルproductSumTypeOp.hsを作成する。

```hs:productSumTypeOp.hs
{-# LANGUAGE TypeOperators #-}

data a * b = a :*: b deriving Show
data a + b = L a | R b deriving Show

x :: (Char * Bool) + (String * Integer)
x = L $ 'c' :*: True
```

演算子と型構築演算子とは、名前空間が異なるので、おなじ名前を使うことができる。対話環境で試してみよう。

```hs
> :load productSumTypeOp.hs
> x
L ('c' :*: True)
> :type x
x :: (Char * Bool) + (String * Integer)
```

型構築演算子の結合力
--------------------

型構築演算子の結合力を変えることもできる。つぎのように結合力の宣言を追加する。

```hs:productSumTypeOp.hs
infixl 7 *
infixl 6 +
```

サンプルの値xの型宣言を書き直す。

```hs:productSumTypeOp.hs
x :: Char * Bool + String * Integer
```

ただし、演算子と型構築演算子の結合力をばらばらに設定することは、たぶん、できない。対話環境で試しておこう。

まとめ
------

関数にたいして演算子が、値構築子にたいして値構築演算子がある。GHCでは言語拡張TypeOperatorsを有効にすることで、型構築子にたいして型構築演算子を使うことができる。