Custom maximumBy and minimumBy in Haskell

Going through the magnificent Haskell Programming from first principles, I'm currently reimplementing functions from standard libraries such as maximumBy and minimumBy.

Here are my current versions of them:

myMaximumBy :: (a -> a -> Ordering) -> [a] -> a
-- maximumBy in Data.List also throws an error on empty lists
myMaximumBy _ [] = error "empty list"
myMaximumBy f (x:xs) =
foldr (\curMax x -> if f x curMax == GT then x else curMax) x xs

myMinimumBy :: (a -> a -> Ordering) -> [a] -> a
myMinimumBy f = myMaximumBy \$ flip f

I know they're not total functions, but I'll get to rewriting them with something like Maybe later on.

I'm happy for any kind of feedback on how to make the code shorter, more readable, or more idiomatic.

• Comments are not for extended discussion; this conversation has been moved to chat. Feb 14 '18 at 12:53

Since maximumBy folds a list of elements into a single element, it seems like a perfect candidate to use foldr or foldr1. However, we would need a -> a -> a instead of a -> a -> Ordering.

Let's have a look at maximum first, which can be defined via max :: Ord a => a -> a -> a:

maximum :: (Foldable t, Ord a) => t a -> a
maximum = foldr1 max

That looks simple enough to adapt to our myMaximumBy:

myMaximumBy :: (a -> a -> Ordering) -> [a] -> a
myMaximumBy f = foldr1 (maxBy f)

We now only need a maxBy that transforms an a -> a -> Ordering into a a -> a -> a:

maxBy :: (a -> a -> Ordering) -> a -> a -> a
maxBy comp x y = if x comp y == LT then y else x

Compared to your original solution, we now

• replaced explicit recursion by foldr1
• introduced a helper function that can get reused

Our new version is now easier to maintain and use.

myMaximumBy can be simplified using foldr1. The list argument can then be made implicit.

myMaximumBy :: (a -> a -> Ordering) -> [a] -> a
myMaximumBy f = foldr1 (\a b -> if f a b == GT then a else b)