<br><br><div class="gmail_quote">On Dec 14, 2007 5:14 AM, Jules Bean &lt;<a href="mailto:jules@jellybean.co.uk">jules@jellybean.co.uk</a>&gt; wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
There are two standard ways to decompose a monad into two adjoint<br>functors: the Kleisli decomposition and the Eilenberg-Moore decomposition.<br><br>However, neither of these categories is a subcategory of Hask in an<br>
obvious way, so I don&#39;t immediately see how to write &quot;f&quot; and &quot;g&quot; as<br>haskell functors.<br><br>Maybe someone else can show the way :)<font color="#888888"></font></blockquote><div><br>One possibility is to extend Haskell&#39;s Functor class. We can define a class of (some) categories whose objects are Haskell types:
<br><br>&gt; class Category mor where<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp; :: mor a a<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; (.) :: mor b c -&gt; mor a b -&gt; mor a c<br><br>The instance for (-&gt;) should be obvious. We can also define an instance for Kleisli operations:
<br><br>&gt; newtype Kleisli m a b = Kleisli { runKleisli :: a -&gt; m b }<br>&gt; instance (Monad m) =&gt; Category (Kleisli m) -- omitted<br><br>Next, a class for (some) functors between these categories:<br><br>&gt; class (Category morS, Category morT) =&gt; Functor f morS morT where
<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; fmap :: morS a b -&gt; morT (f a) (f b)<br><br>Unlike the usual Haskell Functor class, this requires us to distinguish the functor itself from the type constructor involved in the functor.<br><br>Here&#39;s an instance converting Kleisli operations to functions.
<br><br>&gt; instance Monad m =&gt; Functor m (Kleisli m) (-&gt;) where<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; -- fmap :: Kleisli m a b -&gt; (m a -&gt; m b)<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; fmap f = (&gt;&gt;= runKleisli f)<br><br>Going the other way is tricker, because our Functor interface requires a type constructor. We&#39;ll use Id.
<br><br>&gt; newtype Id a = Id { unId :: a }<br>&gt;&nbsp; <br>&gt; instance Monad m =&gt; Functor Id (-&gt;) (Kleisli m) where<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; -- fmap :: (a -&gt; b) -&gt; Kleisli m (Id a) (Id b)<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; fmap f = Kleisli (return . Id . f . unId)
<br><br>Finally, adjunctions between functors:<br><br>&gt; class (Functor f morS morT, Functor g morT morS) <br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; =&gt; Adjunction f g morS morT | f g morS -&gt; morT, f g morT -&gt; morS<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; where<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; leftAdj&nbsp;&nbsp; :: morT (f a) b -&gt; morS a (g b)
<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; rightAdj&nbsp; :: morS a (g b) -&gt; morT (f a) b<br><br>The functional dependency isn&#39;t really justified. It&#39;s there to eliminate ambiguity in the later code.<br><br>The two functors above are adjoint:<br>
<br>&gt; instance (Monad m) =&gt; Adjunction Id m (-&gt;) (Kleisli m) where<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; -- leftAdj :: Kleisli (Id a) b -&gt; (a -&gt; m b)<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; leftAdj f = runKleisli f . Id<br>&gt;<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; -- rightAdj :: (a -&gt; m b) -&gt; Kleisli (Id a) b
<br>&gt;&nbsp;&nbsp;&nbsp;&nbsp; rightAdj f = Kleisli (f . unId)<br><br>So, given two adjoint functors, we have a monad and a comonad. Note, however, that these aren&#39;t necessarily the same as the Haskell classes Monad and Comonad.<br><br>
Here are the monad operations:<br><br>&gt; unit :: (Adjunction f g morS morT) =&gt; morS a (g(f a))<br>&gt; unit = leftAdj id<br>&gt; <br>&gt; extend :: (Adjunction f g morS morT) =&gt; morS a (g(f b)) -&gt; morS (g(f a)) (g(f b))
<br>&gt; extend f = fmap (rightAdj f)<br><br>The monad&#39;s type constructor is the composition of g and f. Extend corresponds to (&gt;&gt;=) with the arguments reversed.<br><br>In our running example, unit and extend have these types:
<br><br>&nbsp;&nbsp;&nbsp; unit&nbsp;&nbsp; :: Monad m =&gt; a -&gt; m (Id a)<br>&nbsp;&nbsp;&nbsp; extend :: Monad m =&gt; (a -&gt; m (Id b)) -&gt; m (Id a) -&gt; m (Id b)<br><br>This corresponds to our original monad, only with the extra Id.<br><br>Here are the comonad operations:
<br><br>&gt; counit :: (Adjunction f g morS morT) =&gt; morT (f(g a)) a<br>&gt; counit = rightAdj id<br>&gt;<br>&gt; coextend :: (Adjunction f g morS morT) =&gt; morT (f(g a)) b -&gt; morT (f(g a)) (f(g b))<br>&gt; coextend f = fmap (leftAdj f)
<br><br>In our running example, counit and coextend have these types:<br><br>&nbsp;&nbsp;&nbsp; counit&nbsp;&nbsp; :: Monad m =&gt; Kleisli m (Id (m a)) a<br>&nbsp;&nbsp;&nbsp; coextend :: Monad m =&gt; Kleisli m (Id (m a)) b -&gt; Kleisli m (Id (m a)) (Id (m b))
<br><br>Thus, m is effectively a comonad in its Kleisli category.<br><br>We can tell a similar story with Comonads and CoKleisli operations, eventually reaching an adjunction like this:<br><br>&gt; instance (Comonad w) =&gt; Adjunction w Id (CoKleisli w) (-&gt;) -- omitted
<br><br>-- <br></div></div>Dave Menendez &lt;<a href="mailto:dave@zednenem.com">dave@zednenem.com</a>&gt;<br>&lt;<a href="http://www.eyrie.org/~zednenem/">http://www.eyrie.org/~zednenem/</a>&gt;