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:
- Die Typensignaturen der erforderlichen Methoden aus der Typklasse können nicht implementiert werden.
- Kann die Typensignaturen implementieren, aber die erforderlichen Gesetze nicht erfüllen.
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
Dies ist ein Kontrafunktor, kein Funktor, in Bezug auf den Typparameter a, weil ain 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 fmapimplementiert werden kann:
data Q a = Q(a -> Int, a)
fmap :: (a -> b) -> Q a -> Q b
fmap f (Q(g, x)) = Q(\_ -> g x, f x) -- this fails the functor laws!
Der merkwürdige Aspekt dieses Beispiels ist, dass wir den richtigen Typ implementieren können , fmapobwohl wir Fmöglicherweise kein Funktor sein können, weil er ain einer kontravarianten Position verwendet wird. Diese fmapoben 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 Beispiel fmap id≠ id, weil let (Q(f,_)) = fmap id (Q(read,"123")) in f "456"ist 123, aber let (Q(f,_)) = id (Q(read,"123")) in f "456"ist 456.
In der Tat Fist nur ein Profunctor, - es ist weder ein Funktor noch ein Kontrafunktor.
Ein rechtmäßiger Funktor, der nicht anwendbar ist, weil die Typensignatur von purenicht implementiert werden kann: Nehmen Sie die Writer-Monade (a, w)und entfernen Sie die Einschränkung, wdie ein Monoid sein sollte. Es ist dann unmöglich, einen Wert vom Typ (a, w)aus zu konstruieren a.
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:
data P a = P ((a -> Int) -> Maybe a)
Der Typkonstruktor Pist ein Funktor, da er anur in kovarianten Positionen verwendet wird.
instance Functor P where
fmap :: (a -> b) -> P a -> P b
fmap fab (P pa) = P (\q -> fmap fab $ pa (q . fab))
Die einzig mögliche Implementierung der Typensignatur von <*>ist eine Funktion, die immer Folgendes zurückgibt Nothing:
(<*>) :: P (a -> b) -> P a -> P b
(P pfab) <*> (P pa) = \_ -> Nothing -- fails the laws!
Diese Implementierung entspricht jedoch nicht dem Identitätsgesetz für anwendbare Funktoren.
- Ein Funktor, der
Applicativeaber kein ist ,Monad weil die Typensignatur von bindnicht implementiert werden kann.
Ich kenne keine solchen Beispiele!
- Ein Funktor, der
Applicativeaber keinMonad weil Gesetze nicht erfüllt werden können, obwohl die Typensignatur von bindimplementiert 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.
data B a = Maybe (a, a)
deriving Functor
instance Applicative B where
pure x = Just (x, x)
b1 <*> b2 = case (b1, b2) of
(Just (x1, y1), Just (x2, y2)) -> Just((x1, x2), (y1, y2))
_ -> Nothing
Es ist etwas umständlich zu beweisen, dass es keine rechtmäßige MonadInstanz gibt. Der Grund für das nicht-monadische Verhalten ist, dass es keine natürliche Möglichkeit gibt, zu implementieren, bindwann eine Funktion zurückkehren f :: a -> B bkönnte Nothingoder Justfür unterschiedliche Werte von a.
Es ist vielleicht klarer zu überlegen Maybe (a, a, a), was auch keine Monade ist, und zu versuchen, dies umzusetzen join. Man wird feststellen, dass es keinen intuitiv vernünftigen Weg zur Implementierung gibt join.
join :: Maybe (Maybe (a, a, a), Maybe (a, a, a), Maybe (a, a, a)) -> Maybe (a, a, a)
join Nothing = Nothing
join Just (Nothing, Just (x1,x2,x3), Just (y1,y2,y3)) = ???
join Just (Just (x1,x2,x3), Nothing, Just (y1,y2,y3)) = ???
-- etc.
In den von angegebenen Fällen ???scheint es klar zu sein, dass wir Just (z1, z2, z3)aus sechs verschiedenen Typwerten keine vernünftige und symmetrische Weise erzeugen können a. Wir könnten sicherlich eine beliebige Teilmenge dieser sechs Werte wählen - zum Beispiel immer die erste MaybeNicht- Leere -, aber dies würde die Gesetze der Monade nicht erfüllen. Die Rücksendung Nothingwird auch nicht den Gesetzen entsprechen.
- Eine baumartige Datenstruktur, die keine Monade ist , obwohl sie assoziativ ist
bind- aber die Identitätsgesetze nicht erfüllt.
Die übliche baumartige Monade (oder "ein Baum mit funktorförmigen Zweigen") ist definiert als
data Tr f a = Leaf a | Branch (f (Tr f a))
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 werden type 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 - es binderfüllt die Natürlichkeits- und Assoziativitätsgesetze, aber seine pureMethode verfehlt eines der Identitätsgesetze. "Semimonaden sind Halbgruppen in der Kategorie der Endofunktoren. Was ist das Problem?" Dies ist die Typklasse Bind.
Der Einfachheit halber definiere ich die joinMethode anstelle von bind:
data Trs f a = Leaf (f a) | Branch (f (Trs f a))
join :: Trs f (Trs f a) -> Trs f a
join (Leaf ftrs) = Branch ftrs
join (Branch ftrstrs) = Branch (fmap @f join ftrstrs)
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)und Maybe (a, a, a)kann eine rechtmäßige MonadInstanz gegeben werden, obwohl sie offensichtlich sind Applicative.
Diese Funktoren haben keine Tricks - nein Voidoder bottomnirgendwo, keine knifflige Faulheit / Strenge, keine unendlichen Strukturen und keine Einschränkungen für Typklassen. Die ApplicativeInstanz ist völlig Standard. Die Funktionen returnund bindkö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 aist 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 Monadaus Polynomtypen rechtmäßige s erzeugen . In all diesen Konstruktionen Mist eine Monade:
type M a = Either c (w, a) wo w ist irgendein Monoid?
type M a = m (Either c (w, a))Wo mist eine Monade und wist ein Monoid
type M a = (m1 a, m2 a)wo m1und m2sind irgendwelche Monaden
type M a = Either a (m a)Wo mist eine Monade?
Die erste Konstruktion ist WriterT w (Either c), die zweite Konstruktion ist WriterT w (EitherT c m). Die dritte Konstruktion ist ein komponentenweise Produkt Monadengemeinschaft: pure @Mist definiert als das komponentenweise Produkt aus pure @m1und pure @m2und join @Mwird durch Weglassen Kreuzproduktdaten definiert (zB m1 (m1 a, m2 a)zugeordnet wird m1 (m1 a)durch den zweiten Teil des Tupels Weglassen):
join :: (m1 (m1 a, m2 a), m2 (m1 a, m2 a)) -> (m1 a, m2 a)
join (m1x, m2x) = (join @m1 (fmap fst m1x), join @m2 (fmap snd m2x))
Die vierte Konstruktion ist definiert als
data M m a = Either a (m a)
instance Monad m => Monad M m where
pure x = Left x
join :: Either (M m a) (m (M m a)) -> M m a
join (Left mma) = mma
join (Right me) = Right $ join @m $ fmap @m squash me where
squash :: M m a -> m a
squash (Left x) = pure @m x
squash (Right ma) = ma
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. Allerdings Either (a, a) (a, a, a)ist monadischen , weil es isomorph zum Produkt von drei Monaden ist a, aund Maybe a. Auch Either (a,a) (a,a,a,a)ist monadisch, weil es isomorph zum Produkt von aund ist Either 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 abeispielsweise zu erhalten Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))und so weiter. Alle derartigen Typkonstruktoren haben (mindestens eine) MonadInstanz.
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. Beispielsweise type F a = Either a (a, a)kann dem Typkonstruktor eine MonadInstanz auf zwei Arten zugewiesen werden: durch Konstruktion 4 unter Verwendung der Monade (a, a)und durch Konstruktion 3 unter Verwendung des Typisomorphismus Either 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 MonadInstanz 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.
* -> *) zu erstellen, für den es keinen geeigneten gibtfmap?