Thread overview
Why is mostNegative!T not of type T?
Oct 17, 2021
SealabJaster
Oct 17, 2021
Ali Çehreli
October 17, 2021
https://github.com/dlang/phobos/blob/master/std/traits.d#L8217


template mostNegative(T)
if (isNumeric!T || isSomeChar!T || isBoolean!T)
{
    static if (is(typeof(T.min_normal)))
        enum mostNegative = -T.max;
    else static if (T.min == 0)
        enum byte mostNegative = 0;
    else
        enum mostNegative = T.min;
}

This is a breakage of the Rule of Least Astonishment: mostNegative!T for all unsigned types yields type byte.

I tried to simplify as follows:

template mostNegative(T)
if (isNumeric!T || isSomeChar!T || isBoolean!T)
{
    static if (is(typeof(T.min_normal)))
        enum mostNegative = -T.max;
    else
        enum mostNegative = T.min;
}

Got errors all the way over in unittests of std.algorithm.comparison.clamp. Looking at the definition of clamp:

auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper)
if (is(typeof(max(min(val, upper), lower))))
in
{
    import std.functional : greaterThan;
    assert(!lower.greaterThan(upper), "Lower can't be greater than upper.");
}
do
{
    return max(min(val, upper), lower);
}

In turn, min and max use mostNegative and makes surprising deductions based on its type. The surprise has been deemed desirable and has become part of an unnecessarily complex mesh.

The failing unittest is:

    int a = -5;
    uint f = 5;
    short b = 6;
    static assert(is(typeof(clamp(f, a, b)) == int));

This is bizarre and very arguably a bug, as one would expect clamping a value of type `whatever` would yield a value of type `whatever`.

October 17, 2021
On Sunday, 17 October 2021 at 19:32:16 UTC, Andrei Alexandrescu wrote:
> ...

I don't really have anything to add other than "wat".

I assume this was (successfully) reviewed as well before being put in, unless it's an ancient relic of past times.

It gives me the same vibes as `ElementType!string == dchar`
October 17, 2021
On 10/17/21 4:00 PM, SealabJaster wrote:
> On Sunday, 17 October 2021 at 19:32:16 UTC, Andrei Alexandrescu wrote:
>> ...
> 
> I don't really have anything to add other than "wat".
> 
> I assume this was (successfully) reviewed as well before being put in, unless it's an ancient relic of past times.
> 
> It gives me the same vibes as `ElementType!string == dchar`

That's my handiwork. Autodecoding and the trainwreck that followed.

I'm really worried that on an idle Sunday morning just reading a couple of Phobos files with an eye for refactoring uncovers a variety of oddities and bugs.
October 17, 2021
> Looking at the definition of clamp:
> 
> auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper)
> if (is(typeof(max(min(val, upper), lower))))
> in
> {
>      import std.functional : greaterThan;
>      assert(!lower.greaterThan(upper), "Lower can't be greater than upper.");
> }
> do
> {
>      return max(min(val, upper), lower);
> }
> 
> In turn, min and max use mostNegative and makes surprising deductions based on its type. The surprise has been deemed desirable and has become part of an unnecessarily complex mesh.
> 
> The failing unittest is:
> 
>      int a = -5;
>      uint f = 5;
>      short b = 6;
>      static assert(is(typeof(clamp(f, a, b)) == int));
> 
> This is bizarre and very arguably a bug, as one would expect clamping a value of type `whatever` would yield a value of type `whatever`.

Update:

https://issues.dlang.org/show_bug.cgi?id=22414
https://github.com/dlang/phobos/pull/8293


October 17, 2021

On 10/17/21 3:32 PM, Andrei Alexandrescu wrote:

>

https://github.com/dlang/phobos/blob/master/std/traits.d#L8217

template mostNegative(T)
if (isNumeric!T || isSomeChar!T || isBoolean!T)
{
    static if (is(typeof(T.min_normal)))
        enum mostNegative = -T.max;
    else static if (T.min == 0)
        enum byte mostNegative = 0;
    else
        enum mostNegative = T.min;
}

This is a breakage of the Rule of Least Astonishment: mostNegative!T for all unsigned types yields type byte.

You need to ask this guy why he did it: https://github.com/dlang/phobos/commit/399fa21dea2910b2fcd525918d5227ec536c49b4#diff-b7918b17cede734a2dd4ecbf2a981d597a5978f52e02f9a3c6f53c9abd797f05R961-R966

;)

-Steve

October 17, 2021
On 10/17/21 4:32 PM, Steven Schveighoffer wrote:
> On 10/17/21 3:32 PM, Andrei Alexandrescu wrote:
>> https://github.com/dlang/phobos/blob/master/std/traits.d#L8217
>>
>>
>> template mostNegative(T)
>> if (isNumeric!T || isSomeChar!T || isBoolean!T)
>> {
>>      static if (is(typeof(T.min_normal)))
>>          enum mostNegative = -T.max;
>>      else static if (T.min == 0)
>>          enum byte mostNegative = 0;
>>      else
>>          enum mostNegative = T.min;
>> }
>>
>> This is a breakage of the Rule of Least Astonishment: mostNegative!T for all unsigned types yields type byte.
> 
> You need to ask this guy why he did it: https://github.com/dlang/phobos/commit/399fa21dea2910b2fcd525918d5227ec536c49b4#diff-b7918b17cede734a2dd4ecbf2a981d597a5978f52e02f9a3c6f53c9abd797f05R961-R966 
> 
> 
> ;)

Before looking: I bet it was me.

After looking: woe is me.

October 17, 2021
On 10/17/21 1:32 PM, Steven Schveighoffer wrote:

> You need to ask this guy why he did it:
> https://github.com/dlang/phobos/commit/399fa21dea2910b2fcd525918d5227ec536c49b4#diff-b7918b17cede734a2dd4ecbf2a981d597a5978f52e02f9a3c6f53c9abd797f05R961-R966 

>
>
> ;)
>
> -Steve

Not the same guy after 13 years. :)

Ali

October 17, 2021
On 10/17/21 5:42 PM, Ali Çehreli wrote:
> On 10/17/21 1:32 PM, Steven Schveighoffer wrote:
> 
>  > You need to ask this guy why he did it:
>  > https://github.com/dlang/phobos/commit/399fa21dea2910b2fcd525918d5227ec536c49b4#diff-b7918b17cede734a2dd4ecbf2a981d597a5978f52e02f9a3c6f53c9abd797f05R961-R966 
> 
>  >
>  >
>  > ;)
>  >
>  > -Steve
> 
> Not the same guy after 13 years. :)
> 
> Ali

Well put. I had some quite naïve ideas back then.