6.10 プロファンクタ
1)双関手の復習
プロファンクタの話をする前に、双関手を再度勉強しておこう。関手と反関手は矢印が逆向きであるという関係を有していたが、双関手とプロファンクタも同じような関係にある。
Haskellでは双関手はData.Bifunctorで定義されている。それによれば、
Prelude Data.Bifunctor> :i Bifunctor class Bifunctor (p :: * -> * -> *) where bimap :: (a -> b) -> (c -> d) -> p a c -> p b d first :: (a -> b) -> p a c -> p b c second :: (b -> c) -> p a b -> p a c
このうち、\(bimap\)を図で表すと次のようになる。
この図から分かるように、二つの対象がそれぞれの対象に対して与えられた射によって計算が行われる。双関手は\(Either\)と\( (,) \)などのデータ型に対して用意されている。
\(bimap\)はこれらのデータ型に対しては次のように定義されている。
instance Bifunctor (,) where bimap f g ~(a, b) = (f a, g b) instance Bifunctor Either where bimap f _ (Left a) = Left (f a) bimap _ g (Right b) = Right (g b)
双関手になれるために少し実行してみよう。二つの関数を必要とするので、これを定義する。左側は3倍する関数としよう。また、右側は文字の長さを計算する関数としよう。
Prelude> import Data.Bifunctor Prelude Data.Bifunctor> a = bimap (*3) length
これを利用してみる。まず、タプルに対してだ。
Prelude Data.Bifunctor> --cf a = ((*3), length) Prelude Data.Bifunctor> a (5, "Bifunctor") (15,9)
次は\(Either\)に対してである。
Prelude Data.Bifunctor> a (Left 5) Left 15 Prelude Data.Bifunctor> a (Right "Either is used as a bifunctor.") Right 30
2)プロファンクタ
プロファンクタの型クラスは次のように定義される。
class Profunctor f where dimap :: (a -> b) -> (c -> d) -> f b c -> f a d
図で示すと
となる。
双関手の時の図と比較すると、\(a\)と\(b\)とが逆になっているのが分かる。今、関数\(\rightarrow\)をプロファンクタのインスタンスとすると、\(f\)のところを関数に変えればよいので、\(dimap\)は次のようになる。
instance Profunctor (->) where dimap :: (a -> b) -> (c -> d) -> (b -> c) -> (a -> d) dimap f g h = g.h.f
となる。
少し利用してみよう。
*Main Data.Bifunctor> a = dimap length odd (`div` 2) *Main Data.Bifunctor> a "Do you like Profunctor?" True
長かったが、関手の説明はこれで一応終了である。