Esiste un concetto di qualcosa come i funzioni di funzione co-applicativa tra comonadi e funzioni?


17

Ogni monade è anche un funzione applicativa e ogni funzione applicativa è una funzione. Inoltre, ogni comonad è un funzione. Esiste un concetto simile tra comonadi e funzioni, qualcosa come il funzione di applicazione congiunta e quali sono le sue proprietà?

FunctorsFunctorsApplicative functors???MonadsComonads

Update: I'd be also interested in possible uses of such a concept.


Are you sure you weren't looking for Comonads -> ??? -> Cofunctors?
josiah

1
@josiah No, as far as I know, comonads are functors, not cofunctors.
Petr Pudlák

1
Isn't divisible that missing piece?
Gus

Risposte:


15

First of all:

Any monad is also an applicative functor and any applicative functor is a functor.

This is true in the context of Haskell, but (reading Applicative as "strong lax monoidal functor") not in general, for the rather trivial reason that you can have "applicative" functors between different monoidal categories, whereas monads (and comonads) are endofunctors.

Further, identifying Applicative with strong lax monoidal functors is slightly misleading, because to justify the name (and the type signature of (<*>)) requires a functor between closed monoidal categories which preserves both the monoidal structure and the internal hom. This could plausibly be called a "lax closed monoidal functor", except that a functor between monoidal closed categories that preserves either property preserves the other in the obvious way. Because Applicative describes only endofunctors on Hask preserving the monoidal structure of (,), its instances gain a lot of properties automatically, including their strength, which can thus be elided.

The apparent connection with Monad is arguably an artifact of the implicit limitations on Applicative causing aspects of their respective monoid structures to coincide, a happy coincidence which unfortunately does not survive dualization.

Just as a comonad on a category C is a monad on Cop, an oplax monoidal functor CD is a lax monoidal functor CopDop. But Haskop is not monoidal closed, and a co-Applicative that doesn't include function application hardly merits the name. Anyway, the result wouldn't be terribly interesting:

class (Functor f) => CoMonoidal f where
    counit :: f () -> ()
    cozip :: f (a, b) -> (f a, f b)

We could instead imagine a notion of "colax closed functor", which would look much more like Applicative if it existed. Unfortunately, Haskop is not (to the best of my knowledge) a closed category at all: newtype Op b a = Op (a -> b) in Hask corresponds to morphisms ba in Haskop, but Op b a doesn't work as an internal hom there--because the arrows are reversed some sort of co-function would be required instead, which we can't define in general for Hask.

If we simply pretend that "colax closed functors" existed for Hask, and furthermore worked in the way we'd naively hope they would, a co-Applicative based on that would probably look like this:

class (Functor f) => CoApplicative f where
    copure :: f a -> a
    coap :: (f a -> f b) -> f (a -> b)

Adding duplicate :: f a -> f (f a) to copure would produce a comonad (assuming the laws are satisfied), of course. But there's no obvious relationship between coap--whatever it might be--and extend :: (f a -> b) -> f a -> f b. Comparing the types it becomes clear that the dualization is happening in different ways: the comonoidal structures underlying duplicate and cozip have little to do with each other or with coap (which probably doesn't make sense anyway), whereas liftA2 (,) and (<*>) are equivalent and can be derived from join.

Another possible way of dualizing Applicative, which has even less to do with comonads, is to consider contravariant monoidal functors:

class (Contravariant f) => ContraMonoidal f where
    contraunit :: f a
    contrazip :: f a -> f b -> f (Either a b)

But this runs afoul of the same issues as above, namely that Haskop is not a closed category. If it were, we would have some type b <~ a such that we could write functions like contracurry :: (Either c b <~ a) -> (c <~ (b <~ a)) and contraapply :: b -> Either a (a <~ b) and so on that actually worked as expected.

If memory serves me, the obstacles here are not specific to Haskell, but rather arise from Hask being cartesian closed (up to the usual hand-waving, of course), a property it shares with most typed lambda calculi, so you're not likely to get very far with a CoApplicative in most settings.

However, in a monoidal closed category more hospitable to dualization you might have better luck. In particular, I believe both Kleisli (Cont r) and its opposite category are monoidal closed, so that might be a better context to explore these ideas.


Comparing your answer to cstheory.stackexchange.com/a/22302/989, it's surprising that you don't dualize products to sums. Of course, you're right that Hask doesn't have categorical sums; but if you're willing to restrict to the category of total programs (like in Agda), let's pretend it's Set for now, that problem disappears. (I'm not saying Set^op is monoidal closed, but I suspect what I'm saying implies it).
Blaisorblade

8

In this post on SO I found an interesting answer - decisive functors. If we replace () by Void, (,) by Either and reverse the arrows, we get:

class Functor f => Decisive f where
    nogood :: f Void -> Void
    orwell :: f (Either s t) -> Either (f s) (f t)

The blog post also gives some laws that decisive functors adhere to.

And, every Comonad is also Decisive:

instance Comonad c => Decisive c where
    nogood = counit
    orwell story = case counit story of
                     Left s  -> fmap (either id (const s)) story
                     Right t -> fmap (either (const t) id) story 

So decisive functors fit in between functors and comonads, just as applicative functors fit in between functors and monads.


6

McBride and Patterson (Section 7) show that an applicative functor, also known as an idiom, is a strong lax monoidal functor. You are looking for a strong colax monoidal functor also known as an strong oplax monoidal functor. As mentioned in a comment, an oplax monoidal functor is a lax monoidal functor between the opposite categories, which ends up being a comonoidal version of a lax monoidal functor.

Draw the diagrams, reverse the arrows!

I'd have to spend a bit of time working out the details to see which it is, and to translate it into a functional programming notion.


For some reason the standard term seems to be "oplax monoidal functor". The idea is a lax monoidal functor between the opposite categories, which ends up being a comonoidal version of a lax monoidal functor. Using "colax comonoidal" is either redundant or equivalent to "lax monoidal".
C. A. McCann

I overdid the "co"-ing. I'll fix my answer.
Dave Clarke
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.