The user 'singpolyma' asked on reddit if there was some general structure underlying:
data FailList a e = Done | Next a (FailList a e) | Fail e
A free monad was suggested, but I wondered if this could be modeled more generally via applicative functors. In Abstracting with Applicatives, Bazerman shows us that the sum of two applicative functors is also an applicative functor, with bias to the left/right, provided we have a natural transformation in the direction of the bias. This sounds like it's what we need! Thus, I started my proposal, but then quickly ran into problems. Can anyone see solutions to these problems?:
Firstly, we start with the definition of the sum of two functors. I started here because we want to model sum types - either successes or successes and a failure.
data Sum f g a = InL (f a) | InR (g a)
And the two functors we want to work with are:
data Success a = Success [a]
data Failure e a = Failure e [a]
Success
is straight forward - it's essentially Const [a]
. However, Failure e
I'm not so sure about. It's not an applicative functor, because pure
doesn't have any definition. It is, however, an instance of 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
Next, we can define the sum of these functors, with a natural transformation from right to left (so a left bias):
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)
And the only thing we now have to do is define our natural transformation, and this is where it all comes crumbling down.
instance Natural Success (Failure e) where
eta (Success a) = Failure ???? a
The inability to create a Failure
seems to be the problem. Furthermore, even being hacky and using ⊥ isn't an option, because this will be evaluated, in the case where you have InR (Success ...) <*> InL (Failure ...)
.
I feel like I'm missing something, but I have no idea what it is.
Can this be done?