On Wed, Mar 25, 2026 at 11:56:21PM +1100, Viktor Dukhovni wrote:
What Olaf seems to have in mind does neet a Monoid, for example:
λ> import Data.Coerce λ> import Data.Foldable λ> import Data.Semigroup λ> λ> minimum :: forall t a. (Ord a, Bounded a, Foldable t) => t a -> a; minimum = (coerce @(Min a) @a) . foldMap' (coerce @a @(Min a)) λ> maximum :: forall t a. (Ord a, Bounded a, Foldable t) => t a -> a; maximum = (coerce @(Max a) @a) . foldMap' (coerce @a @(Max a))
The downside is that of course collections of `String` or other natural unbounded types would then no longer support `maximum` or `minimum`. [ Strings are of course bounded below by "", but I am not aware of standard type classes for one-sided bounds. ] A different interface would be needed for collections of unbounded elements that either returns a 'Maybe a' type, or a sentinel value of the user's choice, or accepts only non-empty foldable types. {-# LANGUAGE ViewPatterns #-} maximum' :: (Ord a, Foldable t) => t a -> Maybe a maximum' (toList -> (x : xs)) = Just $! foldl' max x xs maximum' _ = Nothing minimum' :: (Ord a, Foldable t) => t a -> Maybe a minimum' (toList -> (x : xs)) = Just $! foldl' min x xs minimum' _ = Nothing λ> minimum' [] Nothing λ> maximum' [] :: Maybe String Nothing λ> minimum' ["foo", "bar"] :: Maybe String Just "bar" λ> maximum' ["foo", "bar"] :: Maybe String Just "foo" -- Viktor. 🇺🇦 Слава Україні!