Czy istnieje koncepcja czegoś takiego jak funktony kooperacyjne siedzące między comonadami i funktorami?

17

Każda monada jest również funktorem aplikacyjnym, a każdy funktor aplikacyjny jest funktorem. Ponadto każdy comonad jest funktorem. Czy istnieje podobna koncepcja między comonadami i funktorami, coś w rodzaju funktora kooperacyjnego i jakie są jego właściwości?

FunctorsFunctorsApplicative functors???MonadsComonads

Aktualizacja: Byłbym również zainteresowany możliwymi zastosowaniami takiej koncepcji.

Petr Pudlák
źródło
Czy na pewno nie szukałeś Comonadów -> ??? -> Kooperatorzy?
josiah
1
@josiah Nie, o ile mi wiadomo, comonady są funktorami , a nie kooperatorami.
Petr Pudlák
1
Czy nie można podzielić tego brakującego elementu?
Gus

Odpowiedzi:

15

Po pierwsze:

Każda monada jest również funktorem aplikacyjnym, a każdy funktor aplikacyjny jest funktorem.

Jest to prawdą w kontekście Haskella, ale (czytając Applicativejako „silny luźny funktor monoidalny”) nie ogólnie, z dość trywialnego powodu, że można mieć „aplikacyjne” funktory między różnymi kategoriami monoidalnymi, podczas gdy monady (i comonady) są endofunkorami .

Ponadto identyfikacja za Applicativepomocą silnych luźnych funktorów monoidalnych jest nieco myląca, ponieważ uzasadnienie nazwy (i podpisu typu (<*>)) wymaga funktora między zamkniętymi kategoriami monoidalnymi, który zachowuje zarówno strukturę monoidalną, jak i hom wewnętrzny . Można to prawdopodobnie nazwać „luźnym zamkniętym funktorem monoidalnym”, z tym wyjątkiem, że funktor między monoidalnymi zamkniętymi kategoriami, który zachowuje jedną właściwość, zachowuje drugą w oczywisty sposób . Ponieważ Applicativeopisuje tylko endofunkcje na Hask, zachowujące monoidalną strukturę (,), jego instancje automatycznie zyskują wiele właściwości, w tym ich wytrzymałość , którą można w ten sposób uniknąć.

Pozorny związek z tym Monadjest prawdopodobnie artefaktem ukrytych ograniczeń Applicativepowodowania zbieżności aspektów ich odpowiednich struktur monoidów, szczęśliwy zbieg okoliczności, który niestety nie przeżywa dualizacji.

Tak jak comonada w kategorii jest monadą w C o p , tak opaksoryczny funktor monoidalny C D jest luźnym funktorem monoidalnym C o pD o p . Ale H a s k o p nie jest monoidalnie zamknięty , a nazwa ko- nie zawierająca aplikacji funkcji nie zasługuje na miano. W każdym razie wynik nie byłby wyjątkowo interesujący:CCop CDCopDopHaskopApplicative

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

Zamiast tego moglibyśmy wyobrazić sobie pojęcie „funktora zamkniętego colaxa”, który wyglądałby znacznie bardziej, Applicativegdyby istniał. Niestety, wcale nie jest (według mojej najlepszej wiedzy) kategorią zamkniętą: w H a s k odpowiada morfizmom b a w H a s k o p , ale nie działa jako wewnętrzny hom tam - ponieważ strzałki są odwrócone, zamiast tego wymagana byłaby jakaś funkcja, której nie możemy ogólnie zdefiniować dla H a s k .Haskopnewtype Op b a = Op (a -> b)HaskbaHaskopOp b aHask

Gdybyśmy po prostu udawali, że „funktory zamknięte colax” istniały dla , a ponadto działały w sposób, na jaki naiwnie mamy nadzieję, że tak się stanie, koegzystencja na tym prawdopodobnie wyglądałaby tak:HaskApplicative

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

Oczywiście, dodanie duplicate :: f a -> f (f a)do copuretego spowodowałoby comonadę (zakładając, że prawa są spełnione). Ale nie ma oczywistego związku między - coapczymkolwiek by to nie było - a extend :: (f a -> b) -> f a -> f b. Porównując typy, staje się jasne, że dualizacja odbywa się na różne sposoby: struktury comonoidalne leżące u podstaw duplicatei cozipmające niewiele wspólnego ze sobą lub z nimi coap(co prawdopodobnie i tak nie ma sensu), liftA2 (,)a jednocześnie (<*>)są równoważne i można je wyprowadzić join.

Innym możliwym sposobem dualizacji Applicative, który ma jeszcze mniej wspólnego z comonadami, jest rozważenie przeciwstawnych funktorów monoidalnych:

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

Działa to jednak z tymi samymi problemami, co powyżej, a mianowicie, że nie jest kategorią zamkniętą. Gdyby tak było, mielibyśmy jakiś rodzaj takie, że możemy napisać funkcje, jak i i tak dalej, że faktycznie pracował zgodnie z oczekiwaniami.Haskopb <~ acontracurry :: (Either c b <~ a) -> (c <~ (b <~ a))contraapply :: b -> Either a (a <~ b)

HaskCoApplicative

Jednak w monoidalnej zamkniętej kategorii, bardziej gościnnej dla dualizacji, możesz mieć więcej szczęścia. W szczególności uważam, że obie Kleisli (Cont r)i ich przeciwna kategoria są zamknięte monoidalnie, więc może to być lepszy kontekst do zbadania tych pomysłów.

CA McCann
źródło
Porównując twoją odpowiedź do cstheory.stackexchange.com/a/22302/989 , zaskakujące jest to, że nie dzielisz produktów na sumy. Oczywiście masz rację, że Hask nie ma kategorycznych sum; ale jeśli chcesz ograniczyć się do kategorii programów ogółem (jak w Agdzie), udawajmy, że na razie jest ustawiony, problem znika. (Nie mówię, że Set ^ op jest monoidalnie zamknięty, ale podejrzewam, że sugeruje to, co mówię).
Blaisorblade
8

W tym poście na SO znalazłem ciekawą odpowiedź - decydujące funktory . Jeśli możemy zastąpić ()przez Void, (,)przez Either i odwrócenie strzałki, otrzymujemy:

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

Wpis na blogu zawiera również niektóre przepisy, do których stosują się decydenci.

I każdy Comonadjest również 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 

Tak więc decydujące funktory mieszczą się między funktorami a comonadami, tak jak funktory aplikacyjne mieszczą się między funktorami a monadami.

Petr Pudlák
źródło
6

McBride i Patterson (sekcja 7) pokazują, że aplikacyjny funktor, znany również jako idiom, jest silnym luźnym funktorem monoidalnym . Szukasz silnego funktora monoidalnego colax, znanego również jako silny monoidalny funktor opaksowy . Jak wspomniano w komentarzu, opoidalny funktor monoidalny jest luźnym funktorem monoidalnym pomiędzy przeciwnymi kategoriami, co ostatecznie stanowi comonoidalną wersję luźnego funktora monoidalnego.

Narysuj schematy, odwróć strzałki!

Musiałbym poświęcić trochę czasu na wypracowanie szczegółów, aby zobaczyć, co to jest, i przełożyć je na funkcjonalne pojęcie programowania.

Dave Clarke
źródło
Z jakiegoś powodu standardowym terminem wydaje się być „opoidowy funktor monoidalny”. Chodzi o luźny monoidalny funktor pomiędzy przeciwnymi kategoriami, który ostatecznie jest comonoidalną wersją luźnego funcjonalnego monoidu. Używanie „colax comonoidal” jest albo zbędne, albo równoważne z „lax monoidal”.
CA McCann
Przesadziłem z „co” -ingiem. Naprawię moją odpowiedź.
Dave Clarke