Während ich jemandem erkläre, was eine Typklasse X ist, bemühe ich mich, gute Beispiele für Datenstrukturen zu finden, die genau X sind.
Also bitte ich um Beispiele für:
- Ein Typkonstruktor, der kein Functor ist.
- Ein Typkonstruktor, der ein Functor ist, aber nicht anwendbar.
- Ein Typkonstruktor, der anwendbar ist, aber keine Monade.
- Ein Typkonstruktor, der eine Monade ist.
Ich denke, es gibt überall viele Beispiele für Monaden, aber ein gutes Beispiel für Monaden mit einer gewissen Beziehung zu früheren Beispielen könnte das Bild vervollständigen.
Ich suche nach Beispielen, die einander ähnlich wären und sich nur in Aspekten unterscheiden, die für die Zugehörigkeit zur jeweiligen Typklasse wichtig sind.
Wenn es gelingen könnte, irgendwo in dieser Hierarchie ein Beispiel für Arrow zu finden (liegt es zwischen Applicative und Monad?), Wäre das auch großartig!
haskell
monads
functor
applicative
Rotsor
quelle
quelle
* -> *
) zu erstellen, für den es keinen geeigneten gibtfmap
?a -> String
ist kein Funktor.a -> String
ist ein mathematischer Funktor, aber kein HaskellFunctor
, um es klar zu sagen .(a -> b) -> f b -> f a
Antworten:
Ein Typkonstruktor, der kein Functor ist:
Sie können daraus einen kontravarianten Funktor machen, aber keinen (kovarianten) Funktor. Versuchen Sie zu schreiben
fmap
und Sie werden scheitern. Beachten Sie, dass die kontravariante Funktorversion umgekehrt ist:Ein Typkonstruktor, der ein Funktor ist, aber nicht anwendbar:
Ich habe kein gutes Beispiel. Es gibt
Const
, aber im Idealfall hätte ich gerne ein konkretes Nicht-Monoid und mir fällt keines ein. Alle Typen sind im Grunde genommen numerisch, Aufzählungen, Produkte, Summen oder Funktionen, wenn Sie darauf eingehen. Sie können unten Schweinearbeiter sehen und ich bin nicht einverstanden darüber, obData.Void
es sich um einen handeltMonoid
;Da dies
_|_
in Haskell ein rechtlicher Wert ist und tatsächlich der einzige rechtliche Wert vonData.Void
, erfüllt dies die Monoid-Regeln. Ich bin mir nicht sicher, wasunsafeCoerce
damit zu tun hat, da Ihr Programm nicht mehr garantiert nicht gegen die Haskell-Semantik verstößt, sobald Sie eineunsafe
Funktion verwenden.Im Haskell-Wiki finden Sie einen Artikel unten ( Link ) oder unsichere Funktionen ( Link ).
Ich frage mich, ob es möglich ist, einen solchen Typkonstruktor mit einem umfangreicheren Typsystem wie Agda oder Haskell mit verschiedenen Erweiterungen zu erstellen.
Ein Typkonstruktor, der anwendbar, aber keine Monade ist:
Sie können daraus einen Antrag stellen, mit:
Wenn Sie es jedoch zu einer Monade machen, kann es zu einer Nichtübereinstimmung der Dimensionen kommen. Ich vermute, dass solche Beispiele in der Praxis selten sind.
Ein Typkonstruktor, der eine Monade ist:
Über Pfeile:
Zu fragen, wo ein Pfeil auf dieser Hierarchie liegt, ist wie zu fragen, was für eine Form "rot" ist. Beachten Sie die Art der Nichtübereinstimmung:
aber,
quelle
Either a
als Beispiel für den letzten Fall zu verwenden, da es leichter zu verstehen ist.ZipList
._|_
bewohnt jeden Typ in *, aber der PunktVoid
ist, dass Sie sich nach hinten beugen müssen, um einen zu konstruieren, oder Sie haben seinen Wert zerstört. Aus diesem Grund handelt es sich nicht um eine Instanz von Enum, Monoid usw. Wenn Sie bereits eine haben, lasse ich Sie diese gerne zusammenfügen (geben Sie eineSemigroup
)mempty
, aber ich gebe keine Werkzeuge zum expliziten Konstruieren eines Wertes vom TypVoid
invoid
. Sie müssen die Waffe laden und auf Ihren Fuß richten und selbst den Abzug betätigen.Mein Stil kann durch mein Telefon beengt sein, aber hier geht es weiter.
kann kein Functor sein. Wenn es so wäre, hätten wir es getan
und der Mond würde aus grünem Käse bestehen.
inzwischen
ist ein Funktor
kann aber nicht anwendbar sein, oder wir hätten
und Grün würde aus Mondkäse hergestellt (was tatsächlich passieren kann, aber erst später am Abend).
(Zusätzliche Anmerkung:
Void
, wie inData.Void
einem leeren Datentyp Wenn Sie versuchen , zu verwenden.undefined
Zu beweisen , es ist ein Monoid, ich werde verwendenunsafeCoerce
zu beweisen, dass es nicht ist.)Freudig,
ist in vielerlei Hinsicht anwendbar, z. B. wie Dijkstra es haben würde,
aber es kann keine Monade sein. Um zu sehen , warum nicht, beachten Sie, dass die Rückkehr sein muss ständig
Boo True
oderBoo False
, und damit , dasskann unmöglich halten.
Oh ja, ich hätte es fast vergessen
ist eine Monade. Roll deinen eigenen.
Flugzeug zu fangen ...
quelle
mempty
._|_
Ich glaube, die anderen Antworten haben einige einfache und häufige Beispiele übersehen:
Ein Typkonstruktor, der ein Functor, aber kein Applicative ist. Ein einfaches Beispiel ist ein Paar:
Es gibt jedoch keine Möglichkeit, die
Applicative
Instanz zu definieren, ohne zusätzliche Einschränkungen aufzuerlegenr
. Insbesondere gibt es keine Möglichkeit,pure :: a -> (r, a)
für eine beliebige zu definierenr
.Ein Typkonstruktor, der anwendbar ist, aber keine Monade.Ein bekanntes Beispiel ist ZipList . (Es ist eine
newtype
, die Listen umschließt und verschiedeneApplicative
Instanzen für sie bereitstellt .)fmap
wird wie gewohnt definiert. Aberpure
und<*>
sind definiert alsso
pure
schafft eine unendliche Liste von dem angegebenen Wert zu wiederholen, und<*>
Reißverschluss eine Liste von Funktionen mit einer Liste von Werten - gilt i - ten Funktion i -te Element. (Der Standard<*>
ein[]
erzeugt alle möglichen Kombinationen der Anwendung der i- ten Funktion auf j- te Element.) Es gibt jedoch keine sinnvolle Möglichkeit, eine Monade zu definieren (siehe diesen Beitrag ).Wie passen Pfeile in die Funktor- / Anwendungs- / Monadenhierarchie? Sehen Redewendungen sind ahnungslos, Pfeile sind akribisch, Monaden sind von Sam Lindley, Philip Wadler und Jeremy Yallop promiskuitiv . MSFP 2008. (Sie nennen anwendbare Funktoren Idiome .) Die Zusammenfassung:
quelle
((,) r)
ist ein Funktor, der keine Anwendung ist; Dies liegt jedoch nur daran, dass Sie im Allgemeinen nichtpure
für alle gleichzeitig definieren könnenr
. Es ist daher eine Eigenart der sprachlichen Prägnanz, zu versuchen, eine (unendliche) Sammlung von anwendbaren Funktoren mit einer Definition vonpure
und zu definieren<*>
; in diesem Sinne scheint es nichts mathematisch tief über dieses Gegenbeispiel zu sein , da für jeden Betonr
,((,) r)
kann eine applicative Funktors gemacht werden. Frage: Können Sie sich einen CONCRETE-Funktor vorstellen, der nicht anwendbar ist?Ein gutes Beispiel für einen Typkonstruktor, der kein Funktor ist, ist
Set
: Sie können nicht implementierenfmap :: (a -> b) -> f a -> f b
, da Sie ohne eine zusätzliche EinschränkungOrd b
nicht konstruieren könnenf b
.quelle
fmap
ist nur ein Sprach- / Implementierungsproblem. Es ist auch möglich, sichSet
in die Fortsetzungsmonade einzuwickeln , die daraus eine Monade mit allen Eigenschaften macht, die wir erwarten würden. Siehe diese Frage (obwohl ich nicht sicher bin, ob dies effizient durchgeführt werden kann).b
kann unentscheidbar sein, in diesem Fall können Sie nicht definierenfmap
!Functor
Instanz mit derOrd
Einschränkung definieren können, dies jedoch möglicherweise mit einer anderen DefinitionFunctor
oder einer besseren Sprachunterstützung möglich ist. Tatsächlich ist es mit ConstraintKinds möglich , eine Typklasse zu definieren, die so parametrisiert werden kann.ord
Einschränkung überwinden könnten, bedeutet die Tatsache, dass aSet
keine doppelten Einträge enthalten kann, dassfmap
dies den Kontext verändern könnte. Dies verstößt gegen das Assoziativitätsgesetz.Ich möchte einen systematischeren Ansatz zur Beantwortung dieser Frage vorschlagen und Beispiele zeigen, die keine speziellen Tricks wie die "unteren" Werte oder unendlichen Datentypen oder ähnliches verwenden.
Wann haben Typkonstruktoren keine Typklasseninstanzen?
Im Allgemeinen gibt es zwei Gründe, warum ein Typkonstruktor möglicherweise keine Instanz einer bestimmten Typklasse hat:
Beispiele der ersten Art sind einfacher als Beispiele der zweiten Art, da wir für die erste Art nur prüfen müssen, ob eine Funktion mit einer bestimmten Typensignatur implementiert werden kann, während für die zweite Art nachgewiesen werden muss, dass keine Implementierung erfolgt könnte möglicherweise die Gesetze erfüllen.
Spezifische Beispiele
Ein Typkonstruktor, der keine Funktorinstanz haben kann, weil der Typ nicht implementiert werden kann:
Dies ist ein Kontrafunktor, kein Funktor, in Bezug auf den Typparameter
a
, weila
in einer kontravarianten Position. Es ist unmöglich, eine Funktion mit Typensignatur zu implementieren(a -> b) -> F z a -> F z b
.Ein Typkonstruktor, der kein rechtmäßiger Funktor ist , obwohl die Typensignatur von
fmap
implementiert werden kann:Der merkwürdige Aspekt dieses Beispiels ist, dass wir den richtigen Typ implementieren können ,
fmap
obwohl wirF
möglicherweise kein Funktor sein können, weil era
in einer kontravarianten Position verwendet wird. Diesefmap
oben gezeigte Implementierung ist also irreführend - obwohl sie die richtige Typensignatur hat (ich glaube, dies ist die einzig mögliche Implementierung dieser Typensignatur), sind die Funktorgesetze nicht erfüllt. Zum Beispielfmap id
≠id
, weillet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
ist123
, aberlet (Q(f,_)) = id (Q(read,"123")) in f "456"
ist456
.In der Tat
F
ist nur ein Profunctor, - es ist weder ein Funktor noch ein Kontrafunktor.Ein rechtmäßiger Funktor, der nicht anwendbar ist, weil die Typensignatur von
pure
nicht implementiert werden kann: Nehmen Sie die Writer-Monade(a, w)
und entfernen Sie die Einschränkung,w
die ein Monoid sein sollte. Es ist dann unmöglich, einen Wert vom Typ(a, w)
aus zu konstruierena
.Ein Funktor, der nicht anwendbar ist, weil die Typensignatur von
<*>
nicht implementiert werden kann :data F a = Either (Int -> a) (String -> a)
.Ein Funktor, der nicht rechtmäßig anwendbar ist , obwohl die Typklassenmethoden implementiert werden können:
Der Typkonstruktor
P
ist ein Funktor, da era
nur in kovarianten Positionen verwendet wird.Die einzig mögliche Implementierung der Typensignatur von
<*>
ist eine Funktion, die immer Folgendes zurückgibtNothing
:Diese Implementierung entspricht jedoch nicht dem Identitätsgesetz für anwendbare Funktoren.
Applicative
aber kein ist ,Monad
weil die Typensignatur vonbind
nicht implementiert werden kann.Ich kenne keine solchen Beispiele!
Applicative
aber keinMonad
weil Gesetze nicht erfüllt werden können, obwohl die Typensignatur vonbind
implementiert werden kann.Dieses Beispiel hat einige Diskussionen ausgelöst, daher kann man mit Sicherheit sagen, dass es nicht einfach ist, dieses Beispiel als richtig zu beweisen. Mehrere Personen haben dies jedoch unabhängig voneinander mit unterschiedlichen Methoden überprüft. Siehe Ist `Daten PoE a = Leer | Paar aa` eine Monade? für zusätzliche Diskussion.
Es ist etwas umständlich zu beweisen, dass es keine rechtmäßige
Monad
Instanz gibt. Der Grund für das nicht-monadische Verhalten ist, dass es keine natürliche Möglichkeit gibt, zu implementieren,bind
wann eine Funktion zurückkehrenf :: a -> B b
könnteNothing
oderJust
für unterschiedliche Werte vona
.Es ist vielleicht klarer zu überlegen
Maybe (a, a, a)
, was auch keine Monade ist, und zu versuchen, dies umzusetzenjoin
. Man wird feststellen, dass es keinen intuitiv vernünftigen Weg zur Implementierung gibtjoin
.In den von angegebenen Fällen
???
scheint es klar zu sein, dass wirJust (z1, z2, z3)
aus sechs verschiedenen Typwerten keine vernünftige und symmetrische Weise erzeugen könnena
. Wir könnten sicherlich eine beliebige Teilmenge dieser sechs Werte wählen - zum Beispiel immer die ersteMaybe
Nicht- Leere -, aber dies würde die Gesetze der Monade nicht erfüllen. Die RücksendungNothing
wird auch nicht den Gesetzen entsprechen.bind
- aber die Identitätsgesetze nicht erfüllt.Die übliche baumartige Monade (oder "ein Baum mit funktorförmigen Zweigen") ist definiert als
Dies ist eine kostenlose Monade über den Funktor
f
. Die Form der Daten ist ein Baum, in dem jeder Verzweigungspunkt ein "funktorreicher" Teilbaum ist. Der Standard-Binärbaum würde mit erhalten werdentype f a = (a, a)
.Wenn wir diese Datenstruktur modifizieren, indem wir auch die Blätter in der Form des Funktors herstellen
f
, erhalten wir das, was ich als "Semimonade" bezeichne - esbind
erfüllt die Natürlichkeits- und Assoziativitätsgesetze, aber seinepure
Methode verfehlt eines der Identitätsgesetze. "Semimonaden sind Halbgruppen in der Kategorie der Endofunktoren. Was ist das Problem?" Dies ist die TypklasseBind
.Der Einfachheit halber definiere ich die
join
Methode anstelle vonbind
:Die Asttransplantation ist Standard, aber die Blatttransplantation ist nicht Standard und erzeugt a
Branch
. Dies ist kein Problem für das Assoziativitätsgesetz, sondern verstößt gegen eines der Identitätsgesetze.Wann haben Polynomtypen Monadeninstanzen?
Keiner der Funktoren
Maybe (a, a)
undMaybe (a, a, a)
kann eine rechtmäßigeMonad
Instanz gegeben werden, obwohl sie offensichtlich sindApplicative
.Diese Funktoren haben keine Tricks - nein
Void
oderbottom
nirgendwo, keine knifflige Faulheit / Strenge, keine unendlichen Strukturen und keine Einschränkungen für Typklassen. DieApplicative
Instanz ist völlig Standard. Die Funktionenreturn
undbind
können für diese Funktoren implementiert werden, erfüllen jedoch nicht die Gesetze der Monade. Mit anderen Worten, diese Funktoren sind keine Monaden, weil eine bestimmte Struktur fehlt (aber es ist nicht leicht zu verstehen, was genau fehlt). Zum Beispiel kann eine kleine Änderung im Funktor daraus eine Monade machen:data Maybe a = Nothing | Just a
ist eine Monade. Ein weiterer ähnlicher Funktordata P12 a = Either a (a, a)
ist ebenfalls eine Monade.Konstruktionen für Polynommonaden
Im Allgemeinen sind hier einige Konstruktionen aufgeführt, die
Monad
aus Polynomtypen rechtmäßige s erzeugen . In all diesen KonstruktionenM
ist eine Monade:type M a = Either c (w, a)
wow
ist irgendein Monoid?type M a = m (Either c (w, a))
Wom
ist eine Monade undw
ist ein Monoidtype M a = (m1 a, m2 a)
wom1
undm2
sind irgendwelche Monadentype M a = Either a (m a)
Wom
ist eine Monade?Die erste Konstruktion ist
WriterT w (Either c)
, die zweite Konstruktion istWriterT w (EitherT c m)
. Die dritte Konstruktion ist ein komponentenweise Produkt Monadengemeinschaft:pure @M
ist definiert als das komponentenweise Produkt auspure @m1
undpure @m2
undjoin @M
wird durch Weglassen Kreuzproduktdaten definiert (zBm1 (m1 a, m2 a)
zugeordnet wirdm1 (m1 a)
durch den zweiten Teil des Tupels Weglassen):Die vierte Konstruktion ist definiert als
Ich habe überprüft, ob alle vier Konstruktionen rechtmäßige Monaden produzieren.
Ich vermute, dass es keine anderen Konstruktionen für Polynommonaden gibt. Zum Beispiel wird der Funktor
Maybe (Either (a, a) (a, a, a, a))
durch keine dieser Konstruktionen erhalten und ist daher nicht monadisch. AllerdingsEither (a, a) (a, a, a)
ist monadischen , weil es isomorph zum Produkt von drei Monaden ista
,a
undMaybe a
. AuchEither (a,a) (a,a,a,a)
ist monadisch, weil es isomorph zum Produkt vona
und istEither a (a, a, a)
.Die vier oben gezeigten Konstruktionen ermöglichen es uns, eine beliebige Summe einer beliebigen Anzahl von Produkten einer beliebigen Anzahl von
a
beispielsweise zu erhaltenEither (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
und so weiter. Alle derartigen Typkonstruktoren haben (mindestens eine)Monad
Instanz.Es bleibt natürlich abzuwarten, welche Anwendungsfälle für solche Monaden existieren könnten. Ein weiteres Problem besteht darin, dass die
Monad
über die Konstruktionen 1 bis 4 abgeleiteten Instanzen im Allgemeinen nicht eindeutig sind. Beispielsweisetype F a = Either a (a, a)
kann dem Typkonstruktor eineMonad
Instanz auf zwei Arten zugewiesen werden: durch Konstruktion 4 unter Verwendung der Monade(a, a)
und durch Konstruktion 3 unter Verwendung des TypisomorphismusEither a (a, a) = (a, Maybe a)
. Auch hier ist es nicht sofort offensichtlich, Anwendungsfälle für diese Implementierungen zu finden.Es bleibt eine Frage - bei einem beliebigen Polynomdatentyp zu erkennen, ob eine
Monad
Instanz vorhanden ist. Ich weiß nicht, wie ich beweisen soll, dass es keine anderen Konstruktionen für Polynommonaden gibt. Ich glaube, es gibt bisher keine Theorie, um diese Frage zu beantworten.quelle
B
ist eine Monade. Können Sie dieser Bindung ein Gegenbeispiel gebenPair x y >>= f = case (f x, f y) of (Pair x' _,Pair _ y') -> Pair x' y' ; _ -> Empty
?f
solche auswählen ,f x
dieEmpty
aberf y
eine istPair
, und im nächsten Schritt sind es beidePair
. Ich habe von Hand überprüft, dass die Gesetze für diese Implementierung oder für eine andere Implementierung nicht gelten. Aber es ist ein gutes Stück Arbeit, das zu tun. Ich wünschte, es gäbe einen einfacheren Weg, dies herauszufinden!Maybe
daMaybe
es keine unterschiedlichen Werte enthält, übera
die Sie sich Sorgen machen müssen.Monad
Beispiel besteht aus Funktionenreturn
undbind
daß erfüllen Gesetze. Es gibt zwei Implementierungen vonreturn
und 25 Implementierungenbind
, die den erforderlichen Typen entsprechen. Sie können durch direkte Berechnung zeigen, dass keine der Implementierungen den Gesetzen entspricht. Um den Arbeitsaufwand zu reduzieren, habe ichjoin
anstellebind
der Identitätsgesetze zuerst die Identitätsgesetze verwendet. Aber es war ein gutes Stück Arbeit.Traversable
es nötig ist.m (Either a (m a))
wird mitpure @m
in transformiertm (Either (m a) (m a))
. Dann trivialEither (m a) (m a) -> m a
, und wir können verwendenjoin @m
. Das war die Implementierung, für die ich die Gesetze überprüft habe.