How to convert a failed computation to a successful one and vice versa

224 views Asked by At

This might very well be a solution seeking a problem ... if so, I beg your indulgence!

Possible implementation:

class Switch' f where
  switch :: f a -> f ()

instance Switch' [] where
  switch []     = [()]
  switch (_:_)  = []

instance Switch' Maybe where
  switch Nothing   = Just ()
  switch (Just _)  = Nothing

The interpretation would be: given a successful computation, make it a failing one; given a failing computation, make it a successful one. I'm not sure, but this seems like it might be sort of the opposite of MonadPlus ... if you squint really hard. ???

Is there a standard typeclass or some other implementation for this concept? What would the underlying math look like, if any (i.e. is this a semigroup, a loop, etc.)?

3

There are 3 answers

4
singpolyma On
switch :: (Alternative f, Eq (f a)) => f a -> f ()
switch x | x == empty = pure ()
         | otherwise = empty

or

switch :: (MonadPlus m, Eq (m a)) => m a -> m ()
switch x | x == mzero = return ()
         | otherwise = mzero
0
Petr On

I found a completely different answer, and that is the LogicT monad transformer. It has lnot defined as:

lnot :: MonadLogic m => m a -> m ()

Inverts a logic computation. If m succeeds with at least one value, lnot m fails. If m fails, then lnot m succeeds the value ().

I believe this is exactly what you wanted.

0
Petr On

I've got a generic solution but it can only work for MonadPlus instances that obey the left catch law (and that's probably only a necessary condition, not sufficient):

isZero :: (MonadPlus m) => m a -> m Bool
isZero x = (x >> return False) `mplus` return True

switch :: (MonadPlus m) => m a -> m ()
switch x = isZero x >>= \r -> if r then return () else mzero

It works for STM too.

(For lists it always returns [()] though, I'd say this definition won't work for anything that is satisfies left distribution.)

It's not possible to define it in this way for Applicatives, because switch inspects the value of isZero, and applicatives cannot do that. (And, MonadPlus instances that satisfy the left-catch rule rarely satisfy Applicative laws.)

Anyhow it'd be interesting to see if switch . (switch :: m () -> m ()) = id holds for this definition.