\gdef\Set{\mathrm{\mathbf{Set}}} \gdef\Hask{\mathrm{\mathbf{Hask}}} \gdef\Vect{\mathrm{\mathbf{Vect}}} \gdef\Mon{\mathrm{\mathbf{Mon}}} \gdef\id{\mathrm{id}} \gdef\map{\mathrm{map}} \gdef\op#1{{#1}^{\mathrm{\scriptsize op}}} \gdef\mathcode#1{\raisebox{-0.25em}{\colorbox{eeeeee}{\tt {#1}}}} \gdef\ihom{\operatorname*{hom}} \gdef\hombr#1#2{\left\lbrack {#1},{#2} \right\rbrack} \gdef\blank{\mathord{-}} \gdef\blank2{\mathord{=}}
この記事は以前に公開したGistのまとめ直し1です。
「Comonad
があるならCoapplicative
は無いのか?」
Comonadという型クラスを知れば「Comonad
があるならCoapplicative
は無いのか?」
という素朴な疑問が出てくることは自然な流れのようです。
「Coapplicative
」すなわち「Applicative
の双対」という名前から考えられる型クラスはなんだろうか?
それには便利な利用法があるだろうか?という検討をしたHaskellerは何人もいたようです。私が見つけたものを挙げると、
(実質的には重複したものを除いて、)以下の3箇所でそれぞれ異なるCoapplicative
の考案がされていました。
注意: このリストを作るに際して、時系列あるいは各アイデアの初出などは確認しておりません。
Co-Applicative programming style (Haskell for all, by Gabriella Gonzalez
ここではcontravariantパッケージにあるDivisibleがco-applicativeに相当するのだ、と紹介されています。
class Contravariant f => Divisible f where divide :: (a -> (b,c)) -> f b -> f c -> f a conquer :: f a
-
Coapplicative
に相当するものは何か?という議論がr/haskellでもありました。OP(投稿者)は-- Poster (u/tailcalled) proposed class (Functor f) => Coapplicative f where copure :: f a -> a cozip :: f (Either a b) -> Either (f a) (f b)
と定義して考えています。コメントでは、また別の定義として
-- Comment by u/camcann class (Contravariant f) => Inapplicative f where nil :: f Void contrazip :: (f a, f b) -> f (Either a b)
も提案されました。この
Inapplicative
はcontravariantのDecidableと同等です。
-
ベストアンサーは、
CoApplicative
とCoMonoidal
を以下のように定義し、それらがComonad
と関係しないことを説明しています。つまり、質問者が想像したような、「Comonad
とFunctor
の間にある型クラス」にはならない、としました。2つの異なる定義を考えているのは、
Applicative
とMonoidal
は偶然にも同じ型クラスを定義しているが、 そのアイデアは異なるから、それぞれの双対は一致するとは限らない、としているためです。class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> (f a -> f b) class Functor f => Monoidal f where -- equivalent to Applicative unit :: () -> f () zip :: (f a, f b) -> f (a,b) class Functor f => CoApplicative f where copure :: f a -> a coap :: (f a -> f b) -> f (a -> b) class Functor f => CoMonoidal f where -- NOT equivalent to CoApplicative counit :: f () -> () cozip :: f (a,b) -> (f a, f b)
別の回答者は以下の
Decisive
を挙げています。 残念ながら、参照先として書かれたリンクは切れており、詳細はよくわかりませんでした。- http://sneezy.cs.nott.ac.uk/fplunch/weblog/?p=69
class Functor f => Decisive f where nogood :: f Void -> Void orwell :: f (Either s t) -> Either (f s) (f t)
(この型クラスは2. でも挙げられていました。)
違いを整理する
参考にしたStackOverflowの回答者も言っているように、Applicative
にはいろいろな定義の仕方があり、それによって双対がどんな定義になるのかが変わります。また、定義のどこを変更することを「双対」と呼んでいるのかもさまざまです。
Applicative
には、以下の3通りの同値な定義を与えることができます。
Functor
たちの圏において、Dayを対象のモノイド積とみなしたときのモノイド対象のこと- Laxモノイダル関手 (\Hask, \mathcode{(,)}) \to (\Hask, \mathcode{(,)}) のこと
- Lax閉関手 (\Hask, \mathcode{->}) \to (\Hask, \mathcode{->}) のこと
上記の「Applicative
の双対の候補」たちがApplicative
をどんな抽象化だと考え、何を変更したのかを、これらの定義をもとに検討します。
Applicative
を関手圏のモノイド対象と見たとき
Applicative
を関手圏のモノイド対象とみなすことについては、過去の記事でも取りあげています。
この過去記事で紹介しているように、1. や 2. で”Coapplicative”と呼ばれているcontravariantパッケージのDivisible
クラスとDecidable
クラスは、Contravariant
たちのなす関手圏において、適切なモノイド積を考えたときのモノイド対象になっていました。
名称 | Applicative は |
変更点 |
---|---|---|
Divisible |
モノイド対象 | どのモノイド圏か |
Decidable |
モノイド対象 | どのモノイド圏か |
また、手前味噌ですが、私は以前”モノイドの双対”といえばコモノイドだろうという発想から、Functor
の圏においてDay
をモノイド積としたときのコモノイド対象についても検討しました。Haskellでの定義だけざっと書くと、以下のような型クラスです。
data Day f g x where
Day :: f a -> g b -> (a -> b -> x) -> Day f g x
class Comonad f => Comonoid f where
-- extract :: f a -> a
coapply :: f a -> Day f f a
詳細はこちらをどうぞ: Data.Functor.Day.Comonoid
名称 | Applicative は |
変更点 |
---|---|---|
Comonoid |
モノイド対象 | 同じモノイド圏のコモノイド対象にする |
Applicative
をlaxモノイダル関手と見たとき
Laxモノイダル関手2とは、モノイド圏(C,\otimes_C)から(D,\otimes_D)への関手Fで、モノイド圏としての構造を保つ自然変換unit, mult
を持つようなものです。3
\begin{align*} & \mathrm{unit} \mathrel{\colon} I_D \to F(I_C) \\ & \mathrm{mult} \mathrel{\colon} F(a) \mathrel{\otimes_D} F(b) \to F(a \otimes_C b) \end{align*}
さらなる条件として、このunit, mult
がどちらも自然な同型でもある関手を、強モノイダル関手といいます。
Laxモノイダル関手の双対版として、oplaxモノイダル関手というものもあります。これは、unit, mult
の代わりに、射の向きを逆にしたもの、すなわち
\begin{align*} & \mathrm{opunit} \mathrel{\colon} F(I_C) \to I_D \\ & \mathrm{opmult} \mathrel{\colon} F(a \otimes_C b) \to F(a) \mathrel{\otimes_D} F(b) \end{align*}
を持つという条件を満たす関手のことを指します。例えば、強モノイダル関手はunit, mult
がそれぞれ同型なのでその逆射があり、これによって強モノイダル関手はoplaxモノイダル関手でもあります。
StackOverflowの回答で挙げられたCoMonoidal
クラスは、
上記のlaxモノイダル関手をoplaxモノイダル関手に読み替えて得られます。
名称 | Applicative は |
変更点 |
---|---|---|
CoMonoidal |
laxモノイダル関手(\Hask, \mathcode{(,)}) \to (\Hask, \mathcode{(,)}) | laxをoplaxに |
余談ですが、このCoMonoidal
は「つまらない」クラスです。任意のFunctor f
は以下のようにCoMonoidal
になります。
instance Functor f => CoMonoidal f where
counit :: f () -> ()
= const ()
counit
cozip :: f (a,b) -> (f a, f b)
= (fst <$> fab, snd <$> fab) cozip fab
更に、(oplaxモノイダル関手と見るならば満たされるべき)CoMonoidal
則によって、
任意のCoMonoidal
はこの実装と一致しなければならないことが示せます。
また、詳細のわからなかったDecisive
型クラスも、メソッドの型だけを見れば、oplaxモノイダル関手(\Hask, \mathcode{Either}) \to (\Hask,\mathcode{Either})に相当します。
名称 | Applicative は |
変更点 |
---|---|---|
Decisive |
laxモノイダル関手(\Hask, \mathcode{(,)}) \to (\Hask, \mathcode{(,)}) | laxをoplaxに、モノイド積を(,) からEither に |
謎のCoApplicative
⚠このセクションは自分が勉強したばかりの内容が含まれていて、間違いが含まれるかもしれません!
3.のCoApplicative
は、Applicative
の(<*>) :: f (a -> b) -> f a -> f b
を、
「関数型(->)
にFunctor f
を分配する」というように読んだ上で、その逆方向のcoap :: (f a -> f b) -> f (a -> b)
を持つ型クラスを考えていると見ることができます。
「関数型(->)
にFunctor f
を分配することができる」という性質を一般化したものには、lax閉関手というものがあるようです。そこで、“oplax”閉関手というものがあって、CoApplicative
となるだろうか?と考えたくなります。しかし、「oplax閉関手」というものが明確に定義されたことはなさそうであり、うまく行きそうな定義を考えるのも簡単ではなさそうでした。
より詳しく説明すると、閉関手(closed functor) 4とは、閉圏(closed category)5の構造を保つ関手です。ちょうど、モノイダル関手がモノイド圏の構造を保つ関手であるようなものです。
ここで、閉圏とは、大まかに言えば内部ホム関手\ihom \colon \op{C} \times C \to Cを持つ圏のことです。特に、モノイド閉圏は閉圏になります。
モノイダル関手と同様に、閉関手にもlax閉関手と強閉関手(strong closed functor)の区別が考えられます。
Applicative
は、lax閉関手(\Hask, \ihom = \mathcode{(->)}) \to (\Hask, \ihom = \mathcode{(->)}) と見なすこともできます。(\Hask, \mathcode{(,)}, \mathcode{(->)})はモノイド閉圏であることに加えて、
モノイド閉圏からモノイド閉圏への関手Fがlaxモノイダル関手であることとlax閉関手であることは同値だからです。
一方で、先述したように、“oplax閉関手”なるものがあるのかどうかすら、私にはわかりませんでした。
結局、3.のCoApplicative
は「“oplax閉関手”かもしれない、謎の何か」としか言いようがありません。
名称 | Applicative は |
変更点 |
---|---|---|
CoApplicative |
lax閉関手(\Hask, \mathcode{(->)}) \to (\Hask, \mathcode{(->)}) | (もしかすると)oplax閉関手(?) |
まとめ
名称 | Applicative は |
変更点 |
---|---|---|
Divisible |
モノイド対象 | どのモノイド圏か |
Decidable |
モノイド対象 | どのモノイド圏か |
Comonoid |
モノイド対象 | 同じモノイド圏のコモノイド対象にする |
CoMonoidal |
laxモノイダル関手(\Hask, \mathcode{(,)}) \to (\Hask, \mathcode{(,)}) | laxをoplaxに |
Decisive |
laxモノイダル関手(\Hask, \mathcode{(,)}) \to (\Hask, \mathcode{(,)}) | laxをoplaxに、モノイド積を(,) からEither に |
CoApplicative |
lax閉関手(\Hask, \mathcode{(->)}) \to (\Hask, \mathcode{(->)}) | (もしかすると)oplax閉関手(?) |