Wyjaśniając komuś, czym jest klasa X, staram się znaleźć dobre przykłady struktur danych, które są dokładnie X.
Proszę o przykłady:
- Konstruktor typów, który nie jest Functorem.
- Konstruktor typów, który jest Functorem, ale nie ma zastosowania.
- Konstruktor typów, który jest aplikacyjny, ale nie jest monadą.
- Konstruktor typów, który jest Monadą.
Myślę, że wszędzie jest wiele przykładów Monady, ale dobry przykład Monady z pewnym odniesieniem do poprzednich przykładów może uzupełnić obraz.
Szukam przykładów, które byłyby do siebie podobne, różniąc się tylko aspektami ważnymi dla przynależności do określonej klasy typów.
Gdyby udało się podkraść przykład Strzałki gdzieś w tej hierarchii (czy to między Applicative i Monad?), To też byłoby świetnie!
haskell
monads
functor
applicative
Rotsor
źródło
źródło
* -> *
), dla którego nie ma odpowiedniegofmap
?a -> String
to nie jest funktor.a -> String
jest matematycznym funktorem, ale nie HaskellemFunctor
, żeby być jasnym.(a -> b) -> f b -> f a
Odpowiedzi:
Konstruktor typów, który nie jest Functorem:
Można z niego zrobić funktor kontrowariantny, ale nie funktor (kowariantny). Spróbuj pisać,
fmap
a nie powiedzie się. Zauważ, że przeciwna wersja funktora jest odwrócona:Konstruktor typów, który jest funktorem, ale nie ma zastosowania:
Nie mam dobrego przykładu. Jest
Const
, ale idealnie chciałbym konkretnego nie-monoida i nie mogę myśleć o żadnym. Wszystkie typy są w zasadzie numeryczne, wyliczenia, produkty, sumy lub funkcje, kiedy do tego dojdzie. Możesz zobaczyć poniżej pigworker, a ja nie zgadzam się co do tego, czyData.Void
jestMonoid
;Ponieważ
_|_
jest to wartość prawna w Haskell i faktycznie jedyna wartość prawnaData.Void
, jest to zgodne z zasadami Monoid. Nie jestem pewien, counsafeCoerce
ma z tym wspólnego, ponieważ nie ma już gwarancji, że twój program nie naruszy semantyki Haskell, gdy tylko użyjesz jakiejkolwiekunsafe
funkcji.Zobacz Wiki Haskell, aby znaleźć artykuł na temat dolnej ( link ) lub niebezpiecznych funkcji ( link ).
Zastanawiam się, czy możliwe jest stworzenie takiego konstruktora typu przy użyciu bogatszego systemu typu, takiego jak Agda lub Haskell z różnymi rozszerzeniami.
Konstruktor typów, który jest aplikacją, ale nie monadą:
Możesz zrobić z niego aplikację, używając czegoś takiego:
Ale jeśli uczynisz go monadą, możesz uzyskać niedopasowanie wymiarów. Podejrzewam, że takie przykłady są rzadkie w praktyce.
Konstruktor typów, który jest Monadą:
O strzałkach:
Pytanie o to, gdzie leży strzała w tej hierarchii, jest jak pytanie, jaki jest kształt „czerwonego”. Zwróć uwagę na rodzaj niedopasowania:
ale,
źródło
Either a
na przykład dla ostatniego przypadku, ponieważ jest to łatwiejsze do zrozumienia.ZipList
._|_
zamieszkuje każdy typ w *, ale chodzi oVoid
to, że powinieneś pochylić się do tyłu, aby zbudować jeden lub zniszczyć jego wartość. Dlatego nie jest to instancja Enum, Monoid itp. Jeśli już ją masz, cieszę się, że mogę je złączyć (dając ciSemigroup
)mempty
, ale nie daję żadnych narzędzi do jawnego konstruowania wartości typuVoid
wvoid
. Musisz załadować broń, wycelować ją w stopę i pociągnąć za spust.Mój styl może być ciasny przez mój telefon, ale proszę bardzo.
nie może być Functorem. Gdyby tak było, mielibyśmy
a Księżyc byłby zrobiony z zielonego sera.
W międzyczasie
jest funktorem
ale nie mogą mieć zastosowania, inaczej byśmy mieli
a zielony byłby zrobiony z sera księżycowego (co może się zdarzyć, ale dopiero później wieczorem).
(Dodatkowa uwaga:
Void
ponieważ wData.Void
jest pustym typem danych. Jeśli spróbujesz użyć,undefined
aby udowodnić, że jest to Monoid, użyję,unsafeCoerce
aby udowodnić, że nie jest.)Radośnie,
ma zastosowanie na wiele sposobów, np. tak, jak chciałby to Dijkstra,
ale to nie może być monada. Aby zobaczyć, dlaczego nie, zwróć uwagę, że powrót musi następować w sposób ciągły
Boo True
lubBoo False
odtądnie może być w stanie utrzymać.
O tak, prawie zapomniałem
jest Monadą. Skręć swój własny.
Samolot do złapania ...
źródło
mempty
._|_
Uważam, że w innych odpowiedziach pominięto kilka prostych i powszechnych przykładów:
Konstruktor typów, który jest Functorem, ale nie aplikacją. Prostym przykładem jest para:
Ale nie ma sposobu, jak zdefiniować jego
Applicative
instancję bez nakładania dodatkowych ograniczeńr
. W szczególności nie ma możliwości zdefiniowaniapure :: a -> (r, a)
arbitralnościr
.Konstruktor typów, który jest aplikacyjny, ale nie jest monadą. Dobrze znanym przykładem jest ZipList . (Jest to
newtype
opakowanie, które zawija listy i zapewniaApplicative
dla nich różne wystąpienia).fmap
jest zdefiniowany w zwykły sposób. Alepure
i<*>
są zdefiniowane jakopure
tworzy więc nieskończoną listę, powtarzając podaną wartość, i<*>
zamyka listę funkcji z listą wartości - stosuje i-tą funkcję do i-tego elementu. (Standard<*>
na[]
tworzy wszystkie możliwe kombinacje zastosowania i- tej funkcji do j-tego elementu.) Ale nie ma rozsądnego sposobu na zdefiniowanie monady (zobacz ten post ).Jak strzałki pasują do hierarchii funktorów / aplikacji / monad? Zobacz idiomy są nieświadome, strzały są drobiazgowe, monady są rozwiązłe przez Sama Lindleya, Philipa Wadlera, Jeremy Yallopa. MSFP 2008. (Nazywają idiomy funktorów aplikacyjnych .) Streszczenie:
źródło
((,) r)
jest funktorem, który nie ma zastosowania; ale dzieje się tak tylko dlatego, że ogólnie nie można zdefiniowaćpure
wszystkichr
naraz. Jest to zatem dziwactwo zwięzłości języka, próby zdefiniowania (nieskończonego) zbioru funktorów aplikacyjnych z jedną definicjąpure
i<*>
; w tym sensie, że nie wydaje się być cokolwiek matematycznie głęboko o tym kontrprzykład, ponieważ za każdym betonier
,((,) r)
może być to funktor aplikacyjny. Pytanie: Czy możesz pomyśleć o funktorze BETON, który nie działa?Dobrym przykładem konstruktora typów, który nie jest funktorem, jest
Set
: Nie można zaimplementowaćfmap :: (a -> b) -> f a -> f b
, ponieważ bez dodatkowego ograniczeniaOrd b
nie można skonstruowaćf b
.źródło
fmap
jest tylko kwestią języka / implementacji. Możliwe jest również owinięcieSet
monady kontynuacyjnej, która tworzy monadę ze wszystkimi właściwościami, których moglibyśmy się spodziewać, zobacz to pytanie (chociaż nie jestem pewien, czy można to zrobić skutecznie).b
może być nierozstrzygalna, w takim przypadku nie można zdefiniowaćfmap
!Functor
instancji zOrd
ograniczeniem, ale może to być możliwe przy innej definicjiFunctor
lub lepszej obsłudze języka. W rzeczywistości za pomocą ConstraintKinds można zdefiniować klasę typów, którą można sparametryzować w ten sposób.ord
ograniczenie, fakt, żeSet
nie może zawierać zduplikowanych wpisów, oznacza, żefmap
może on zaokrąglić kontekst. To narusza prawo dotyczące stowarzyszania.Chciałbym zaproponować bardziej systematyczne podejście do odpowiedzi na to pytanie, a także pokazać przykłady, w których nie stosuje się żadnych specjalnych sztuczek, takich jak „najniższe” wartości, nieskończone typy danych lub cokolwiek podobnego.
Kiedy konstruktory typów nie mają instancji klas typów?
Zasadniczo istnieją dwa powody, dla których konstruktor typów może nie mieć instancji określonej klasy typu:
Przykłady pierwszego rodzaju są łatwiejsze niż przykłady drugiego rodzaju, ponieważ w przypadku pierwszego rodzaju wystarczy sprawdzić, czy można zaimplementować funkcję z danym typem podpisu, natomiast w przypadku drugiego rodzaju musimy udowodnić, że nie ma implementacji mógłby ewentualnie spełniać prawa.
Konkretne przykłady
Konstruktor typów, który nie może mieć instancji funktora, ponieważ nie można zaimplementować typu:
Jest to kontrafunkcjonariusz, a nie funktor, w odniesieniu do parametru type
a
, ponieważa
w pozycji przeciwstawnej. Niemożliwe jest wdrożenie funkcji z podpisem typu(a -> b) -> F z a -> F z b
.Konstruktor typów, który nie jest zgodnym z prawem funktorem, mimo że
fmap
można zaimplementować podpis typu :Ciekawy aspekt tego przykładu jest to, że można wdrożyć
fmap
od odpowiedniego typu, chociażF
nie może być ewentualnie funktor ponieważ wykorzystujea
w kontrawariantny pozycji. Ta implementacjafmap
pokazana powyżej jest myląca - mimo że ma prawidłową sygnaturę typu (uważam, że jest to jedyna możliwa implementacja tego typu sygnatury), prawa funktora nie są spełnione. Na przykładfmap id
≠id
, ponieważlet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
jest123
, alelet (Q(f,_)) = id (Q(read,"123")) in f "456"
jest456
.W rzeczywistości
F
jest tylko profesorem - nie jest ani funktorem, ani contrafunctor.Zgodny z prawem funktor, który nie ma zastosowania, ponieważ
pure
nie można zaimplementować podpisu typu : weź monadę Writer(a, w)
i usuń ograniczenie, którew
powinno być monoidem. Wówczas niemożliwe jest skonstruowanie wartości typu(a, w)
na podstawiea
.Funktora że nie jest aplikacyjnych , ponieważ podpis typu
<*>
nie mogą być realizowane:data F a = Either (Int -> a) (String -> a)
.Funktor, który nie jest zgodny z prawem, mimo że można zastosować metody klasy typu:
Konstruktor typów
P
jest funktorem, ponieważ używaa
tylko w pozycjach kowariantnych.Jedyną możliwą implementacją podpisu typu
<*>
jest funkcja, która zawsze zwracaNothing
:Ale ta implementacja nie spełnia prawa tożsamości dla funktorów aplikacyjnych.
Applicative
nie jest,Monad
ponieważbind
nie można zaimplementować podpisu typuNie znam takich przykładów!
Applicative
ale nie jest tak,Monad
ponieważ prawa nie mogą być spełnione, nawet jeślibind
można zaimplementować podpis typu .Ten przykład wywołał sporo dyskusji, więc można śmiało powiedzieć, że udowodnienie poprawności tego przykładu nie jest łatwe. Ale kilka osób zweryfikowało to niezależnie różnymi metodami. Zobacz Is `data PoE a = Empty | Sparować monadę? do dodatkowej dyskusji.
Dość trudne jest udowodnienie, że nie ma zgodnej z prawem
Monad
instancji. Przyczyną nie-monadycznego zachowania jest to, że nie ma naturalnego sposobu implementacji,bind
kiedy funkcjaf :: a -> B b
może zwrócićNothing
lubJust
dla różnych wartościa
.Być może bardziej zrozumiałe jest rozważenie
Maybe (a, a, a)
, które również nie jest monadą, i próba wdrożeniajoin
tego. Okaże się, że nie ma intuicyjnie rozsądnego sposobu wdrożeniajoin
.W przypadkach wskazanych przez
???
wydaje się jasne, że nie możemy wytwarzaćJust (z1, z2, z3)
w żaden rozsądny i symetryczny sposób z sześciu różnych wartości typua
. Z pewnością moglibyśmy wybrać dowolny arbitralny podzbiór tych sześciu wartości - na przykład zawsze przyjmujemy pierwszą niepustąMaybe
- ale to nie spełniałoby praw monady. Powrót nieNothing
będzie również zgodny z prawem.bind
ale nie spełnia praw tożsamości.Zwykła monada drzewiasta (lub „drzewo z gałęziami w kształcie funktora”) jest zdefiniowana jako
Jest to wolna monada nad funktorem
f
. Kształt danych jest drzewem, w którym każdy punkt rozgałęzienia jest „funktorem” poddrzewa. Standardowe drzewo binarne można uzyskać za pomocątype f a = (a, a)
.Jeśli zmodyfikujemy tę strukturę danych, tworząc również liście w kształcie funktora
f
, otrzymamy coś, co nazywam „semimonadą” - ma onobind
spełnienie praw natury i asocjatywności, ale jejpure
metoda nie spełnia jednego z praw tożsamości. „Semimonady to półgrupy w kategorii endofunkcji, w czym problem?” To jest klasa typuBind
.Dla uproszczenia definiuję
join
metodę zamiastbind
:Szczepienie gałęzi jest standardowe, ale szczepienie liści jest niestandardowe i powoduje
Branch
. Nie stanowi to problemu dla prawa o stowarzyszeniu, ale łamie jedno z praw dotyczących tożsamości.Kiedy typy wielomianowe mają instancje monad?
Żaden z funktorów
Maybe (a, a)
iMaybe (a, a, a)
może być udzielona legalnemuMonad
instancji, choć są oczywiścieApplicative
.Te funktory nie mają żadnych sztuczek - żadnych
Void
anibottom
nigdzie, żadnych trudnych lenistw / ścisłości, żadnych nieskończonych struktur i żadnych ograniczeń klasowych.Applicative
Przykład całkowicie standardowy. Funkcjereturn
ibind
mogą być zaimplementowane dla tych funktorów, ale nie spełniają praw monady. Innymi słowy, te funktory nie są monadami, ponieważ brakuje konkretnej struktury (ale nie jest łatwo zrozumieć, czego dokładnie brakuje). Na przykład niewielka zmiana funktora może przekształcić go w monadę:data Maybe a = Nothing | Just a
jest monadą. Kolejnym podobnym funktoremdata P12 a = Either a (a, a)
jest także monada.Konstrukcje monad wielomianowych
Ogólnie rzecz biorąc, oto niektóre konstrukcje, które produkują zgodne
Monad
z prawem typy wielomianowe. We wszystkich tych konstrukcjachM
jest monada:type M a = Either c (w, a)
gdziew
jest jakikolwiek monoidtype M a = m (Either c (w, a))
gdziem
jest dowolna monada iw
dowolna monoidtype M a = (m1 a, m2 a)
gdziem1
im2
są dowolne monadytype M a = Either a (m a)
gdziem
jest jakakolwiek monadaPierwsza konstrukcja to
WriterT w (Either c)
druga konstrukcjaWriterT w (EitherT c m)
. Trzecia konstrukcja jest produktem monadpure @M
składającym się z komponentów : jest definiowana jako produkt składający się zpure @m1
ipure @m2
, ijoin @M
jest definiowany przez pominięcie danych między produktami (np.m1 (m1 a, m2 a)
Jest odwzorowanym1 (m1 a)
przez pominięcie drugiej części krotki):Czwarta konstrukcja jest zdefiniowana jako
Sprawdziłem, czy wszystkie cztery konstrukcje produkują legalne monady.
I przypuszczać, że nie istnieją żadne inne zbudowania dla wielomianu monad. Na przykład funktor
Maybe (Either (a, a) (a, a, a, a))
nie jest uzyskiwany przez żadną z tych konstrukcji, a zatem nie jest monadyczny. JednakEither (a, a) (a, a, a)
jest monadycznego ponieważ jest izomorficzna z produktem trzech monada
,a
orazMaybe a
. PonadtoEither (a,a) (a,a,a,a)
jest monadyczny, ponieważ jest izomorficzny w stosunku do produktua
iEither a (a, a, a)
.Cztery powyższe konstrukcje pozwolą nam uzyskać dowolną sumę dowolnej liczby produktów dowolnej liczby
a
, na przykładEither (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
i tak dalej. Wszystkie tego typu konstruktory będą miały (przynajmniej jedną)Monad
instancję.Oczywiście okaże się, jakie przypadki użycia mogą istnieć dla takich monad. Inną kwestią jest to, że
Monad
przypadki wyprowadzone za pomocą konstrukcji 1-4 nie są na ogół wyjątkowe. Na przykład, konstruktorowi typutype F a = Either a (a, a)
można nadaćMonad
instancję na dwa sposoby: przez konstrukcję 4 za pomocą monady(a, a)
i przez konstrukcję 3 za pomocą izomorfizmu typuEither a (a, a) = (a, Maybe a)
. Znów znalezienie przypadków użycia dla tych implementacji nie jest od razu oczywiste.Pozostaje pytanie - biorąc pod uwagę arbitralny wielomianowy typ danych, jak rozpoznać, czy ma on
Monad
instancję. Nie wiem, jak udowodnić, że nie ma innych konstrukcji monad wielomianowych. Nie sądzę, aby jak dotąd istniała jakaś teoria odpowiadająca na to pytanie.źródło
B
to monada. Czy możesz podać kontrprzykład na to powiązaniePair x y >>= f = case (f x, f y) of (Pair x' _,Pair _ y') -> Pair x' y' ; _ -> Empty
?f
taką, któraf x
jest,Empty
alef y
jestPair
, a na następnym etapie obie sąPair
. Sprawdziłem ręcznie, czy przepisy nie dotyczą tej implementacji ani żadnej innej implementacji. Ale jest to sporo pracy, aby to zrobić. Chciałbym, żeby istniał łatwiejszy sposób, aby to rozgryźć!Maybe
ponieważMaybe
nie zawiera różnych wartości, którymia
należy się martwić.Monad
Instancji składa się z funkcjamireturn
ibind
że prawa zadowolić. Istnieją dwie implementacjereturn
i 25 implementacjibind
pasujących do wymaganych typów. Możesz wykazać na podstawie bezpośrednich obliczeń, że żadna z implementacji nie jest zgodna z przepisami. Aby zmniejszyć ilość wymaganej pracy, użyłemjoin
zamiastbind
i stosować przepisy ustawowe tożsamości pierwszy. Ale to sporo pracy.Traversable
jest to potrzebne.m (Either a (m a))
przekształca się za pomocąpure @m
wm (Either (m a) (m a))
. Więc trywialnieEither (m a) (m a) -> m a
i możemy użyćjoin @m
. To było wdrożenie, dla którego sprawdziłem przepisy.