← 前回: 圏論から見るHaskellのFunctor
前回記事ではHaskellにおけるFunctorを圏論的な視点から考察してみました。今回はその流れでApplicativeについて見てみたいと思います。
Applicativeはモナドや(強自己)関手に比べてとても新しい概念であり、圏論の範囲でモナドや強自己関手について調べられたのが1960~70年代、モッジ (Eugenio Moggi) によってモナドがプログラミングに応用されたのが1990年前後であるのに比べて、Applicativeが論文に登場したのは2008年とつい最近のことでした (しかも圏論からではなくプログラミング論側、Functional Pearlとしての掲載でした)。
圏論から見た場合、applicative functorを端的に表現するとモノイダル強自己関手というように言い表せます。ApplicativeはFunctorが持っていなかったモノイダルな性質を獲得し、そのためにプログラミングにおいても有用な性質を獲得しています。
定義の都合上、定義文中ではモノイダル圏上で定義していますが、本稿の内容の範囲ではカルテシアン閉圏に読み替えて構いません。カルテシアン閉圏については前回を参照してください。
対称モノイダル閉圏
関手とは圏と圏の間に定まる関係であり、圏の構造のみからその内容が決定されます。言い換えると、型の対応
モノイダル圏
同型
また、
強モノイダルな関手はlaxかつoplaxなモノイダル関手ということができます。ストリクトモノイダルは強モノイダルのさらに特別な場合であるので、これもlaxでもoplaxでもあります。
カルテシアン積を持つ圏の間の関手は、積に関する普遍性から自然に
モノイダル閉圏
HaskellにおけるApplicativeは、Functor
さらに、Applicative
この
が成り立つため、
とできるため、従ってApplicative
となります。
また、
となり、ここから
定義に見られる通り、applicative functorはモノイダル関手的な性質と強自己関手的な性質を両方持ち合わせているため、それら単体の性質および相互的な性質の数々が絡み合っています。この節ではそれらをなるべく個別に考察してみたいと思います。
このとき、
加えて、以下の図式も併せて可換になります:
モノイダル関手の間の自然変換が上図のようなコヒーレンス条件を満たすとき、
カルテシアン閉圏
このとき、
カルテシアン閉圏の上のapplicative functor
定義から、この射は
とおきます。このとき、次の2つの図式が可換になります:
既に前節で見た通り
関数適用に対応する
カルテシアン閉圏
一方で、
実際にApplicativeの例としてZipListを見てみましょう。ZipListはデータとしてはリストと同一ですが、Applicativeの実装にzippingを採用したものです。
Applicativeの実装は次のようになっています:
pure x = ZipList (repeat x)
liftA2 f (ZipList xs) (ZipList ys) = ZipList (zipWith f xs ys)
この実装の本質であるzipWithの挙動ですが、リスト
>>> let f = undefined
>>> zipWith f [] undefined
[]
ZipListはおそらくMonadにできますが、およそ有用でないらしく2021年12月現在baseなどには実装されていません。
Applicativeの利用について、最たる例がおそらくtraverseでしょう。この関数はTraversableクラスのメソッドであり、Traversable tに対して次のような型を持ちます:traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
具体的な例としては、IOモナドとListに対して次のような関数が与えられます:traverse :: (a -> IO b) -> [a] -> IO [b]
TraversableはMonadよりよほど特殊なことを要求しているためここでは扱いませんが、事実としてTraversableがApplicativeのサブクラスであることにより次のような関手がTraversableとして実装できます。以下で圏
これらがtraverseの実装を持つことは、applicative functorの性質 (特にlaxモノイダル関手としての性質)と余積の持つ普遍性によって示され、モナドであることは必要としていません。
Haskellのリストは無限リストも許容していますがTraversableの実装を持ちます。実装を読む限りはTraversableがFoldableを要求していることと関連していそうですが、これに関しては調べ切れていません。
圏論側に立ってapplicative functorがどのようなものかを観察してきました。Applicativeはどうしてもちょっと弱いMonadというような立ち位置にいるため、いまひとつ立ち位置のつかめないクラスに今後もしばらくなりつづけることと思われます。しかし、圏論的視点に立てばそもそもApplicativeの本質はモナドっぽい部分には無く、モノイダル関手、あるいはモノイダル変換としての構造および機能にあることが分かります。
そもそもAppicativeという言葉自体が、内実がapplication=適用にあることを主張していますから、その意味でもこれをモナドの腐ったのみたいに見る方がおかしいということですね (そんな見方してる人はいない)。