Der Benutzer 'singpolyma' fragte auf reddit nach, ob es eine allgemeine Struktur gibt, die folgendem zugrunde liegt:
data FailList a e = Done | Next a (FailList a e) | Fail e
Es wurde ein Free-Monad vorgeschlagen, aber ich fragte mich, ob dies allgemeiner über applikative Funktoren modelliert werden könnte. In Abstrahieren mit Applikatoren zeigt uns Bazerman, dass die Summe zweier applikativer Funktoren ebenfalls ein applikativer Funktor ist, mit Bias nach links/rechts, vorausgesetzt, wir haben eine natürliche Transformation in Richtung des Bias. Das klingt so, als ob das das ist, was wir brauchen! Also begann ich mit meinem Vorschlag, stieß dann aber schnell auf Probleme. Kann jemand Lösungen für diese Probleme sehen?:
Zunächst starten wir mit der Definition der Summe zweier Funktoren. Ich habe hier angefangen, weil wir Summentypen modellieren wollen - entweder Erfolge oder Erfolge und ein Scheitern.
data Sum f g a = InL (f a) | InR (g a)
Und die beiden Funktoren, mit denen wir arbeiten wollen, sind:
data Success a = Success [a]
data Failure e a = Failure e [a]
Success
ist einfach - es ist im Wesentlichen Const [a]
. Allerdings bin ich mir bei Failure e
nicht sicher. Es ist kein applikativer Funktor, weil pure
keine Definition hat. Es ist jedoch eine Instanz von Apply:
instance Functor Success where
fmap f (Success a) = Success a
instance Functor (Failure e) where
fmap f (Failure e a) = Failure e a
instance Apply (Failure e) where
(Failure e a) <.> (Failure _ b) = Failure e a
instance Apply Success where
(Success a) <.> (Success b) = Success (a <> b)
instance Applicative Success where
pure = const (Success [])
a <*> b = a <.> b
Dann können wir die Summe dieser Funktoren definieren, mit einer natürlichen Transformation von rechts nach links (also einem Linksbias):
instance (Apply f, Apply g, Applicative g, Natural g f) => Applicative (Sum f g) where
pure x = InR $ pure x
(InL f) <*> (InL x) = InL (f <*> x)
(InR g) <*> (InR y) = InR (g <*> y)
(InL f) <*> (InR x) = InL (f <.> eta x)
(InR g) <*> (InL x) = InL (eta g <.> x)
Und das Einzige, was wir jetzt tun müssen, ist unsere natürliche Transformation zu definieren, und hier bricht alles zusammen.
instance Natural Success (Failure e) where
eta (Success a) = Failure ???? a
Die Unfähigkeit, ein Failure
zu erzeugen, scheint das Problem zu sein. Darüber hinaus ist selbst ein hackiges Verwenden von keine Option, weil dies im Fall, dass InR (Success ...) <*> InL (Failure ...)
aufgerufen wird, ausgewertet wird.
Ich habe das Gefühl, dass mir etwas fehlt, aber ich habe keine Ahnung, was es ist.
Ist dies machbar?