haskellmonoidsargmaxsemigrouptype-synonyms

What is the purpose of the ArgMin and ArgMax type synonyms in Data.Semigroup?


The base library in Haskell has the following type synonyms in Data.Semigroup:

type ArgMin a b = Min (Arg a b)

type ArgMax a b = Max (Arg a b) 

Here are links to the haddocks: ArgMin and ArgMax

What is the purpose of these two type synonyms? Where can they be used effectively?

It might be helpful to include an explanation of what the argmin and argmax functions do in mathematics, and how that is related to these type synonyms.


Here's a little extra information so you don't have to jump to Hackage.

Here's the definition of Arg:

-- | 'Arg' isn't itself a 'Semigroup' in its own right, but it can be
-- placed inside 'Min' and 'Max' to compute an arg min or arg max.
data Arg a b = Arg a b

Its doc string suggests that ArgMin and ArgMax can be placed inside of Min and Max to compute an arg min or an arg max.

Min and Max look like the following:

newtype Min a = Min { getMin :: a }

The Semigroup instance is interesting:

instance Ord a => Semigroup (Min a) where
  (<>) = coerce (min :: a -> a -> a)

It looks like it is using min as (<>).

We can look at what the Ord instance looks like for Arg, since it is relevant here:

instance Ord a => Ord (Arg a b) where
  Arg a _ `compare` Arg b _ = compare a b
  min x@(Arg a _) y@(Arg b _)
    | a <= b    = x
    | otherwise = y
  max x@(Arg a _) y@(Arg b _)
    | a >= b    = x
    | otherwise = y

This appears to only run the comparison on the first type argument to Arg.


Solution

  • I suppose it's one of those things that exist in Haskell because the theoretical concept exists. I'm not sure if these types have much practical use, but they do illustrate just how extensive the concepts of semigroups and monoids are in relation to programming.

    Imagine, for example, that you need to pick the longest of two names, name1 and name2, both of them String values. You can use the Semigroup instance of ArgMax for that:

    Prelude Data.Semigroup> Max (Arg (length name1) name1) <> Max (Arg (length name2) name2)
    Max {getMax = Arg 5 "Alice"}
    

    After that, it's just a question of unwrapping "Alice" from its container.

    As Willem Van Onsem points out in the comments, you can use ArgMax and ArgMin to pick the maximum or minimum item, according to some attribute of the item, but still keeping the original item around.