elm/core

Basics.clamp doesn't passthrough NaN

malaire opened this issue · 2 comments

If clamp is given NaN as third argument it should return it unchanged, but instead it returns the given upper limit:

elm repl
> clamp -100 100 (0/0)
100 : Float

I don't understand why this happens. clamp is defined as (source)

clamp : number -> number -> number -> number
clamp low high number =
  if lt number low then
    low
  else if gt number high then
    high
  else
    number

Since both comparisons should be False, number should be returned unchanged, but that doesn't happen.

I can't test gt myself, but its corresponding > does return False as expected:

elm repl
>  (0/0) > 100
False : Bool

Maybe related to #1050

Thanks for reporting this! To set expectations:

  • Issues are reviewed in batches, so it can take some time to get a response.
  • Ask questions a community forum. You will get an answer quicker that way!
  • If you experience something similar, open a new issue. We like duplicates.

Finally, please be patient with the core team. They are trying their best with limited resources.

Basics.clamp is compiled to:

var $elm$core$Basics$clamp = F3(
    function (low, high, number) {
        return (_Utils_cmp(number, low) < 0) ? low : ((_Utils_cmp(number, high) > 0) ? high : number);
    });

and because _Utils_cmp does:

function _Utils_cmp(x, y, ord)
{
    if (typeof x !== 'object')
    {
        return x === y ? /*EQ*/ 0 : x < y ? /*LT*/ -1 : /*GT*/ 1;
    }

with both x === y and x < y returning false with NaN, it will return GT, therefore the clamp higher value will be returned:

> Basics.compare (0/0) 100
GT : Order

(>) (0/0) 100 returns False because it is optimized by the compiler to (0 / 0) > 100 in javascript without using _Utils_cmp.

If you trick the compiler to avoid the optimization, you get True for NaN > 100:

> (>) (0/0) 100
False : Bool

> gt = (>)
<function> : comparable -> comparable -> Bool

> gt (0/0) 100
True : Bool