Freer Effectsが、だいたいわかった: 11-2 TypeOperators拡張
目次
(0). 導入
-
Freeモナドの概要
- Freeモナドとは
- FreeモナドでReaderモナド、Writerモナドを構成する
- 存在型(ExistentialQuantification拡張)の解説
- 型シノニム族(TypeFamilies拡張)の解説
- データ族(TypeFamilies拡張)の解説
- 一般化代数データ型(GADTs拡張)の解説
- ランクN多相(RankNTypes拡張)の解説
-
FreeモナドとCoyoneda
- Coyonedaを使ってみる
- FreeモナドとCoyonedaを組み合わせる
- いろいろなモナドを構成する
-
Freerモナド(Operationalモナド)でいろいろなモナドを構成する
- FreeモナドとCoyonedaをまとめて、Freerモナドとする
- Readerモナド
- Writerモナド
- 状態モナド
- エラーモナド
-
モナドを混ぜ合わせる(閉じた型で)
- Freerモナドで、状態モナドとエラーモナドを混ぜ合わせる
- 両方のモナドを一度に処理する
- それぞれのモナドを、それぞれに処理する
- Freerモナドで、状態モナドとエラーモナドを混ぜ合わせる
- 存在型による拡張可能なデータ構造(Open Union)
- 追加の言語拡張
- ScopedTypeVariables拡張
- TypeOperators拡張
- KindSignatures拡張
- DataKinds拡張
- MultiParamTypeClasses拡張
- FlexibleInstances拡張
- OVERLAPSプラグマ
- Open Unionを型によって安全にする
- モナドを混ぜ合わせる(開いた型で)
- FreeモナドとOpen Unionを組み合わせる
- 状態モナドにエラーモナドを追加する
- Freer Effectsで、IOモナドなどの、既存のモナドを使用する
- 関数を保管しておくデータ構造による効率化
- いろいろなEffect
- 関数handleRelayなどを作成する
- NonDetについて、など
直積と直和
Haskellでは型を組み合わせることができる。組み合わせかたには「ふたつの型の両方の値をもつ型をつくる」、または、「ふたつの型の、どちらかの値をもつ型をつくる」というやりかたがある。それぞれ、「直積」「直和」のようによぶ。
直積
ふたつの型の両方の値をもつ値は、つぎのように表現できる。
> ('c', True)
('c',True)
> :type it
it :: (Char, Bool)
代数的データ型では、つぎのようになる。
> 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型との直積型は、つぎのようになる。
(Bool, T)
この型に属する値は、つぎのようになる。
(False, A)
(False, B)
(False, C)
(True, A)
(True, B)
(True, C)
このように2個の値をもつ型と、3個の値をもつ型との直積である型は、6個の値をもつ。「2かける3で6」だ。
直和
ふたつの型の、どちらかの型の値をもつ型の値は、つぎのように表現できる。
> Left 'c' :: Either Char Bool
Left 'c'
> :type it
Either Char Bool
代数的データ型では、つぎのようになる。
> 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型との直和型は、つぎのようになる。
Either Bool T
この型に属する値は、つぎのようになる。
Left False
Left True
Right A
Right B
Right C
このように2個の値をもつ型と、3個の値をもつ型との直和である型は、5個の値をもつ。「2たす3で5」だ。
直積型と直和型を定義する
タプルとEither型は、そのまま直積と直和である。また、代数的データ型も、そのままで直積と直和を表現できる。なので、ここで新たな「直積型」「直和型」を定義する理由はないが、説明の都合上、定義することとする。ファイル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
試してみる。
> :load productSum.hs
> x
L (Product 'c' True)
> :type x
x :: Sum (Product Char Bool) (Product String Integer)
値構築演算子
値構築演算子を使うと、直積型はより読みやすくなる。productSum.hsの、データ型Productの定義を修正する。
data Product a b = a :*: b deriving Show
値構築演算子は、ふつうの演算子と名前空間がおなじなので、区別するために、その名前には:(コロン)から、はじまる識別子を使う。最後にも:(コロン)をつけたのは、そのほうが見た目のバランスがいいからで、とくに深い意味はない。サンプルの値xの定義も修正する。
x = L $ 'c' :*: True
> :reload
> x
L ('c' :*: True)
> :type x
x :: Sum (Product Char Bool) (Product String Integer)
型構築演算子
さて、関数にたいして演算子があり、値構築子にたいして値構築演算子がある。それでは、型構築子にたいして型構築演算子は使えるだろうか。使える。そう、GHCならね。つぎのようにファイル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
演算子と型構築演算子とは、名前空間が異なるので、おなじ名前を使うことができる。対話環境で試してみよう。
> :load productSumTypeOp.hs
> x
L ('c' :*: True)
> :type x
x :: (Char * Bool) + (String * Integer)
型構築演算子の結合力
型構築演算子の結合力を変えることもできる。つぎのように結合力の宣言を追加する。
infixl 7 *
infixl 6 +
サンプルの値xの型宣言を書き直す。
x :: Char * Bool + String * Integer
ただし、演算子と型構築演算子の結合力をばらばらに設定することは、たぶん、できない。対話環境で試しておこう。
まとめ
関数にたいして演算子が、値構築子にたいして値構築演算子がある。GHCでは言語拡張TypeOperatorsを有効にすることで、型構築子にたいして型構築演算子を使うことができる。