Say I have a map with measurable objects, and I'd like the maximum of their widths, defaulting to 0. The machinery of Foldable
(foldMap
) and Semigroup
(Max
) seems perfect, except that I can't seem to introduce the arbitrary lower bound.
data Thing
width :: Thing -> Double
maxWidth :: Map k Thing -> Double
maxWidth things = getMax . foldMap (Max . width) $ things
This rightly complains about the missing Bounded
instance for Double
, since the Monoid
instance of Max a
uses mempty = minBound
.
I see that the source for Data.Foldable
uses a different definition of newtype Max
to implement maximum
. That variant would do nicely, but it seems to not be exported:
maxWidth things = fromMaybe 0 . getMax . foldMap (Max . Just . width) $ things
You can use the Option
monoid to get a monoid instance for any Semigroup
. Option a
is just a newtype wrapper over Maybe a
, but it's Monoid
instance only requires Semigroup a
rather than Monoid a
. So we can use it with Max
:
maximumMaybe :: (Ord a, Foldable t) => t a -> Maybe a
maximumMaybe = fmap getMax . getOption . foldMap (Option . Just . Max)
If you want a default value for the empty list case, you can use fromMaybe
:
maximumWithDefault :: (Ord a, Foldable t) => a -> t a -> a
maximumWithDefault d = fromMaybe d . maximumMaybe
Another option is to just use maximumMay :: (Ord a, Foldable t) => t a -> Maybe a
from the safe
package.
If you are using base-4.11.0
or higher, you no longer need the Option a
type since the restriction on Monoid (Maybe a)
has been lifted to Semigroup a
. So as of base-4.11.0
, which came with GHC 8.4.1, you can write:
maximumMaybe :: (Ord a, Foldable t) => t a -> Maybe a
maximumMaybe = fmap getMax . foldMap (Just . Max)