Question

Let us say, I have two Maybe Bool value and I want to achieve the following functionality:

  • If both are Just values, I want to perform an || between them the values.
  • If one of them is Nothing and the other one is Just value, then I want the Just value as the output.
  • If both of them is Nothing, then I want Just False as the output.

I know that this can be achieved using pattern matching. But is it possible to use any monadic functions to acheive the result ?

liftM2 works for this case:

ghci> liftM2 (||) (Just True) (Just False)
Just True

But liftM2 will produce Nothing when any of the one input is Nothing (for which I want the other Just value). i.e:

ghci> liftM2 (||) (Nothing) (Just False)
Nothing

But I want Just False in the above case.

Is it possible to do this using any monadic function ?

Was it helpful?

Solution 2

No. The monad instance has no notion of emptiness1, so it can't check on Nothing and replace the value in that case.

What you basically need is the monoid instance; that has Nothing as its identity element so whatever you combine with Nothing will just come out as is.

instance (Monoid a) => Monoid (Maybe a)

Unfortunately, Bool is in itself not a monoid. Well, actually it is a monoid! but not in a unique way, so they couldn't've chosen any particular instances. But with newtype wrappers, those are in Data.Monoid:

newtype Any = Any { getAny :: Bool }
instance Monoid Any

let's try it...

Prelude Data.Monoid> fmap getAny $ (Just $ Any True) <‌> (Just $ Any False)
Just True
Prelude Data.Monoid> fmap getAny $ (Nothing) <‌> (Just $ Any False)
Just False


1Of course, there's fail... but that's a historic accident.

OTHER TIPS

As it stands, we don't even need to invoke the monadic apparatus. According to your specification, "Nothing" can be mapped to "False" and "Just b" to "b":

mbor a b = Just (flat a || flat b)
   where flat = maybe False id

As @leftaroundabout correctly points out this essentially is what the Monoid Any instance does.

A very useful operator here is <|> from the Alternative class in Control.Applicative. For Maybe, it works like this:

Just a  <|> _       = Just a
Nothing <|> Just a  = Just a
Nothing <|> Nothing = Nothing

We can also take advantage of the fact that x || x == x is always true. This lets us write the following:

orMaybe a b = liftA2 (||) (a <|> b) (b <|> a) <|> Just False

If both a and b are Just x, the liftA2 (||) results in Just (a || b). If one of them is Nothing, the (a <|> b) and (b <|> a) turn into either both a or both b, resulting in Just (a || a) or Just (b || b). Finally, if both are Nothing, we get liftA2 (||) Nothing Nothing which leads to Nothing. The final <|> Just False then turns the whole expression into Just False.

Now, I think this is a fun exercise to work through. But would I actually use this code? No! For Maybe, Nothing usually signifies failure and propagates; since you're using some very non-standard behavior, it's better to be explicit and pattern-match all the cases instead.

Note: liftA2 comes from Control.Applicative. It's just like liftM but for applicatives; I used it for consistency with <|>. You could have used fmap as well.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top