I’m also strongly for ‘clamp :: (Ord a) => (a, a) -> a -> a’.
Even if we don’t resolve it now, I do want to mention that, in discussing this with some acquaintances recently, we agreed that one-sided clamps likely warrant a home in ‘Data.Ord’ as well:
atLeast :: (Ord a) => a -> a -> a
atLeast = max
{-# INLINE atLeast #-}
atMost :: (Ord a) => a -> a -> a
atMost = min
{-# INLINE atMost #-}
clamp :: (Ord a) => (a, a) -> a -> a
clamp (lower, upper) = atLeast lower . atMost upper
While their implementations are identical to ‘max’ and ‘min’, semantically they privilege their arguments differently, serving as documentation of intent in code like ‘nonnegative = fmap (atLeast 0)’. The hope is that this may help reduce bugs caused by the common error of mixing up ‘min’ and ‘max’, owing to the unfortunate false friendship between “at least/most” and “the least/most”.