I want to get the minimum of two maybe values, or if one is nothing get the non-nothing one, or return nothing if both inputs are nothing. I can write a simple function to do this, but I suspect there is a way to do this without writing a custom function. Sorry, if this is a petty question, but is there a simpler way than using this custom function?
minMaybe :: Ord a => Maybe a -> Maybe a -> Maybe a
minMaybe Nothing b = b
minMaybe a Nothing = a
minMaybe (Just a) (Just b) = Just $ min a b
It is possible to satisfy the specification using operators from Control.Applicative
.
myMin :: Ord x => Maybe x -> Maybe x -> Maybe x
myMin a b = min <$> a <*> b <|> a <|> b
where the <|>
for Maybe
implements "preference"
Nothing <|> b = b
a <|> _ = a
The thing is
min <$> Just a <*> Just b = Just (min a b)
but
min <$> Just a <*> Nothing = Nothing
which has resulted in some incorrect answers to this question. Using <|>
allows you to prefer the computed min
value when it's available, but recover with either individual when only one is Just
.
But you should ask if it is appropriate to use Maybe
in this way. With the inglorious exception of its Monoid
instance, Maybe
is set up to model failure-prone computations. What you have here is the extension of an existing Ord
with a "top" element.
data Topped x = Val x | Top deriving (Show, Eq, Ord)
and you'll find that min
for Topped x
is just what you need. It's good to think of types as not just the representation of data but the equipment of data with structure. Nothing
usually represents some kind of failure, so it might be better to use a different type for your purpose.
You cannot use the Applicative
, or Monad
instance for this, since any Nothing
in those contexts will have your total result being a Nothing
. That being said, the term "simpler" is highly opinionated, and your function is fine as it is.
You can write it using the Alternative
instance of Maybe
:
minMaybe a b = liftA2 min a b <|> a <|> b
Alternatively, you could use maxBound
as default, so it'll always choose the other:
minMaybe a b = liftA2 min (d a) (d b)
where d x = x <|> Just maxBound
But I don't recommend that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With