Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Minimum of Two Maybes

Tags:

haskell

maybe

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
like image 806
clay Avatar asked Sep 10 '14 15:09

clay


3 Answers

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.

like image 51
pigworker Avatar answered Oct 23 '22 10:10

pigworker


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.

like image 38
Zeta Avatar answered Oct 23 '22 10:10

Zeta


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.

like image 32
bennofs Avatar answered Oct 23 '22 11:10

bennofs