Open treeowl opened 6 years ago
This sounds like: "??? is to Divisible as Apply is to Applicative". It feels like it should be in semigroupoids
, but isn't. Similarly: "??? is to Decideable as Alt is to Alternative". If we put them here instead of in semigroupoids we can set the superclass relationships up right, too. Once #57 is done we can also define analogous operators. Maybe these classes?
class Contravariant f => Divide f where
divide :: (a -> (b, c)) -> f b -> f c -> f a
class Divide f => Divisible f where
conquer :: f a
class Divide f => Decide f where
choose :: (a -> Either b c) -> f b -> f c -> f a
class (Divisible f, Decide f) => Decideable f where
lose :: (a -> Void) -> f a
I'm pattern-matching off the semigroupoids diagram, and skipping over Plus
, because we don't seem to have an equivalent to MonadPlus
.
It would be great if someone who knew the maths could check over this hierarchy and make sure that none of the splits result in lawless typeclasses.
@ekmett, any thoughts?
I'm actually exploring something similar in the hkd
package, where it seems this notion of Semidivisible
becomes way more common. I will admit I'm a bit uncomfortable with the explosion of names, instances, and granularity.
I went the other way with the Comonad
class at one point, basically making Extend
a superclass for a long time, but then anything that switched back and forth between Monad
and Comonad
failed to land on the nose, due to my inability to put in a matching superclass above Monad.
So my thoughts right now are that there is a tension between making a more broadly applicable class and making a class that people really can understand how to use and that matches up with what is available on the other side of the mirrored class hierarchy.
I think my biggest objection to conquer
always being there is that when programming by types, it can just go anywhere. The same, of course, applies to mempty
, while pure
(usually) doesn't do that.
It strikes me as quite strange and somewhat unfortunate that
conquer
is a method ofDivisible
. I really think it should be in a subclass. The fundamental idea ofDivisible
is it's for problems that can be handled in arbitrary pieces. The notion of a default handler looks like extra power on top of that. Here's a simple example:To get
conquer
, this needs aMonoid
constraint.So I think
Divisible
should dropconquer
, and there should be a separate class for that.