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 a
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:
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 , fmap
obwohl wir F
möglicherweise kein Funktor sein können, weil er a
in einer kontravarianten Position verwendet wird. Diese fmap
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 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 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 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 P
ist ein Funktor, da er a
nur 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
Applicative
aber kein ist ,Monad
weil die Typensignatur von bind
nicht implementiert werden kann.
Ich kenne keine solchen Beispiele!
- Ein Funktor, der
Applicative
aber keinMonad
weil Gesetze nicht erfüllt werden können, obwohl die Typensignatur von bind
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.
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 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ückkehren f :: a -> B b
könnte Nothing
oder Just
fü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 Maybe
Nicht- Leere -, aber dies würde die Gesetze der Monade nicht erfüllen. Die Rücksendung Nothing
wird 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 bind
erfüllt die Natürlichkeits- und Assoziativitätsgesetze, aber seine pure
Methode 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 join
Methode 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 Monad
Instanz gegeben werden, obwohl sie offensichtlich sind Applicative
.
Diese Funktoren haben keine Tricks - nein Void
oder bottom
nirgendwo, keine knifflige Faulheit / Strenge, keine unendlichen Strukturen und keine Einschränkungen für Typklassen. Die Applicative
Instanz ist völlig Standard. Die Funktionen return
und bind
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 Konstruktionen M
ist eine Monade:
type M a = Either c (w, a)
wo w
ist irgendein Monoid?
type M a = m (Either c (w, a))
Wo m
ist eine Monade und w
ist ein Monoid
type M a = (m1 a, m2 a)
wo m1
und m2
sind irgendwelche Monaden
type M a = Either a (m a)
Wo m
ist 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 @M
ist definiert als das komponentenweise Produkt aus pure @m1
und pure @m2
und join @M
wird 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
, a
und Maybe a
. Auch Either (a,a) (a,a,a,a)
ist monadisch, weil es isomorph zum Produkt von a
und 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 a
beispielsweise zu erhalten Either (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. Beispielsweise type F a = Either a (a, a)
kann dem Typkonstruktor eine Monad
Instanz 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 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.
* -> *
) zu erstellen, für den es keinen geeigneten gibtfmap
?