ファンクター則
Functor のインスタンスはファンクター則の2つの性質を満たす必要がある。 自前のファンクターを作る時はこの性質を満たす必要がある。
第1法則
idでファンクター値を写した場合、ファンクター値が変化してはいけない
id
は引数をそのまま返すだけ(恒等写像)の関数。
Maybe
が第一法則を満たしているか確認する。
instance Functor Maybe where fmap _ Nothing = Nothing fmap f (Just a) = Just (f a)
f
を id
に変えてみる
instance Functor Maybe where fmap id Nothing = Nothing fmap id (Just a) = Just (id a)
確認
*Main Lib> fmap id (Just 3) Just 3 *Main Lib> id (Just 3) Just 3 *Main Lib> fmap id [1..5] [1,2,3,4,5] *Main Lib> fmap id [] [] *Main Lib> fmap id Nothing Nothing
第2法則
すべてのファンクター値 x に対して fmap (f . g) x = fmap f (fmap g x) が成り立つ
2つの関数 f g において、
f と g の合成関数でファンクター値を写したもの
まず g, 次に f でファンクター値を写したもの
上記の2つが等しいことを要求する。
Maybe がこの法則を満たしているか確認する
1つめ
fmap ((f . g) x) (Just x)
Just ((f . g) x)
Just (f (g x))
2つめ
fmap f (fmap g (Just x))
fmap f (Just (g x))
Just (f (g x))
最終的に等しくなることが証明できる。
ファンクター則を破る
Functor のインスタンスなのにファンクター則を満たしていない例
data CMaybe a = CNothing | CJust Int a deriving (Show)
Maybe に似た型で、Just 部分のフィールドが2つある。1つ目のフィールドは Int で何らかのカウンタになる。
確認
*Main> CNothing CNothing *Main> CJust 0 "haha" CJust 0 "haha" *Main> :t CNothing CNothing :: CMaybe a *Main> :t CJust 0 "haha" CJust 0 "haha" :: CMaybe [Char] *Main> CJust 100 [1,2,3] CJust 100 [1,2,3]
これを Functor のインスタンスにする
instance Functor CMaybe where fmap f CNothing = CNothing fmap f (CJust counter x) = CJust (counter + 1) (f x)
fmap するたびに counter が増えていく実装となっている。
*Main> fmap (++"ha") (CJust 0 "ho") CJust 1 "hoha" *Main> fmap (++"he") (fmap (++"ha") (CJust 0 "ho")) CJust 2 "hohahe" *Main> fmap (++"blah") CNothing CNothing
ファンクター則を満たしているかどうか調べる
*Main> fmap id (CJust 0 "haha") CJust 1 "haha" *Main> id (CJust 0 "haha") CJust 0 "haha"
id でファンクター値を写した結果と、単純に id をファンクター値に適用した結果が等しくないので CMaybe はファンクター則を満たしていない。
ファンクター則を満たしていると、その型の挙動についてある種の信頼がおけるようになる。
所感
ファンクター則を満たしていない状態でも実装できてしまうのはなんでなのか。 コンパイラがチェックしてもよさそうなのになーと思った。

- 作者: MiranLipovaca
- 出版社/メーカー: オーム社
- 発売日: 2017/07/14
- メディア: Kindle版
- 購入: 4人 クリック: 9回
- この商品を含むブログを見る