Functorをちゃんと見ておく
先日はMonoidとFunctorのことで玉砕してしまいました。色々教えていただいたので、ここらが攻め時ということで、もう一押しです。
Monad, Monoid, Functor, Applicativeなどを見ていていつもこんがらがっちゃうのが、それらが一体何のサブクラスなのかということです...
Monoidはある集合G、その集合(型)におけるある関数f::(G a) => a -> a -> a、そして集合Gの要素で、関数fに対して特殊な振る舞いをする要素eに関する記述でした。これは一体何のサブクラスなんでしょう?あるいは、一体何について属性を記述しているのでしょう?やっぱり、特定の型と、特定の関数のペアについての属性を記述しているということなのでしょうか…
Functorは前回_さんにコメントしていただいたように、
class Functor f where fmap :: (a -> b) -> f a -> f b
で、かつ、以下のルールを満たす:
fmap id == id fmap (f . g) == fmap f . fmap g
これは、一体何のサブクラスなんでしょう?クラスの定義自身をみると、Functor fはaをパラメタ型として持つ型で、fmapによってf aの中にあるaに(a->b)な関数を適用する方法を定義していると読めます…
だとしたら、Functorは関数に関する属性を記述しているのではなくて、fというコンテナ型についての属性を記述していると見たほうが良いですよね?なのになんでFunctorなんでしょう?混乱します。この認識は間違ってるのでしょうか?C++で使われるFunctorと同系のものだと思う時点で間違っているということなのかもしれませんね...
MonadもFunctorのように
class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a
コンテナ型についての記述のように見えますね。
class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b
そうやって見ると、Applicativeはコンテナ型の属性を記述しているFunctorを更にスペシャライズしているように見えます。
更に見ていくと、Data.FoldableやData.Traversableなんかが出てきます。こいつらもコンテナ型についての記述をしているようです。ここで疑問になるのは、なんで、FoldableやTraversableはDataの下なのに、Functor, Applicative, MonadはControlの下なんでしょう?まぁMonadは例外処理とかかわりがあったり、Haskellではコード実行の順序を決定するのに使われるという意味ではたしかにControlとも言えなくもないような…
ちなみにFunctorのインスタンスになっている型は結構あって:
Functor IO Functor Id Functor Maybe Functor ReadP Functor ReadPrec Functor STM Functor ZipList Functor [] Ix i => Functor (Array i) Functor (Const m) Functor (Either a) Functor (ST s) Functor (ST s) Monad m => Functor (WrappedMonad m) Functor ((,) a) Functor ((->) r) Arrow a => Functor (WrappedArrow a b)
だそうで、僕が知らなかったのが、MaybeとかIOまでFunctorになっていて、liftMがfmapでできてしまうということのようです。
今まで、
foo = do f <- monadicAction return $ functionalCall f
とやっていたのが、実は
foo = liftM(functionalCall) monadicAction
とやったり、
foo = fmap (functionalCall) monadicAction
と書いたりできるようです。ぼくはliftMをあまり使ったことがなかったので、これはliftMのことを理解するのにも役立ちました。
Functor *1 1 ==> 34
となったりするのも興味深い限りです。
きょうはこのへんで、
ではでは。
*1:,) a)やFunctor((->)r)もあるので、 fmap (+1) (1, 2) ==> (1, 3) だったり、 (fmap (+1) (\a -> a + 1