Reputation: 48654
Let us say, I have two Maybe Bool
value and I want to achieve the following functionality:
Just
values, I want to perform an ||
between them the values.Nothing
and the other one is Just
value, then I want the Just
value as the output.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 ?
Upvotes: 5
Views: 5087
Reputation: 36339
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.
Upvotes: 9
Reputation: 120711
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.
Upvotes: 6
Reputation: 68152
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.
Upvotes: 6