質問

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 ?

役に立ちましたか?

解決 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.

他のヒント

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top