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