clamp function in base

Jon Purdy evincarofautumn at gmail.com
Thu Aug 27 18:34:05 UTC 2020


I see a discussion of how the one-sided clamps can be implemented only for
floating-point using NaN, but my concern is the API for Ord types
generally. ‘clamp (lo, hi) x = min hi (max x lo)’ allows ‘atLeast lo =
clamp (lo, 0/0)’ and ‘atMost hi = clamp (0/0, hi)’, which is excellent for
providing reasonable NaN handling, but doesn’t say anything about e.g.
integers. It does point to the fact that the correct implementations of
‘atLeast’ and ‘atMost’, for consistency with the correct ‘clamp’ for
floats, are actually these:

atMost = min
atLeast = flip max -- rather than just ‘max’

To me, that’s another argument that these should be considered, since it’s
the kind of subtle distinction that libraries should be handling for users,
along the same lines as the stability of ‘min’ & ‘max’ (namely: ‘a == b ==>
(min a b, max a b) == (a, b)’). Again, this doesn’t necessarily need to go
in the same MR, but they are closely related.

If they were included, it would be necessary to include a note in the
documentation that the correct order is ‘atMost hi . atLeast lo’ if someone
is applying them separately, but that ‘clamp’ should be preferred for
automatically doing this.

I don’t know offhand how this would be disrupted down the line if we
changed the ‘Ord’ instance for floats to use the IEEE-754 total ordering,
but that should also be considered for all these functions.


On Thu, Aug 27, 2020 at 9:05 AM Carter Schonwald <carter.schonwald at gmail.com>
wrote:

> actually, if you look at the associated ticket, we have a version of clamp
> that gives the right way to derive the onesided behaviors for even floating
> point! (and has the correct / desirable behavior in the presence of NANs! )
>
> On Wed, Aug 26, 2020 at 8:38 PM Jon Purdy <evincarofautumn at gmail.com>
> wrote:
>
>> 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”.
>>
>>
>> On Sun, Aug 16, 2020 at 2:43 AM Henning Thielemann <
>> lemming at henning-thielemann.de> wrote:
>>
>>>
>>> On Fri, 14 Aug 2020, Sandy Maguire wrote:
>>>
>>> > It seems to me that base is missing the very standard function `clamp
>>> :: Ord a => a -> a -> a -> a`:
>>> >
>>> > ```haskell
>>> > clamp :: Ord a => a -> a -> a -> a
>>> > clamp low high = min high .max low
>>> > ```
>>>
>>>
>>>
>>> https://hackage.haskell.org/package/utility-ht-0.0.15/docs/Data-Ord-HT.html#v:limit
>>> _______________________________________________
>>> Libraries mailing list
>>> Libraries at haskell.org
>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>>>
>> _______________________________________________
>> Libraries mailing list
>> Libraries at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/libraries/attachments/20200827/5e8bf2f4/attachment.html>


More information about the Libraries mailing list