Thread overview
Goofy code means opportunities in language design
Sep 17, 2020
Timon Gehr
Sep 17, 2020
Avrina
Sep 17, 2020
Stefan Koch
Sep 18, 2020
Jacob Carlborg
September 17, 2020
Making a pass through Phobos code is very instructive. I find all kinds of goofy code that really means the language doesn't allow people to do what they want to do. So they need to write all sorts of oddities.

Consider this (from std.algorithm.iteration.cache):

private struct _Cache(R, bool bidir)
{
    ...
    private R source;
    static if (isInfinite!R)
        enum empty = false;
    else
        bool empty() @property
        {
            return source.empty;
        }
    ...
}

These lines appear over and over in Phobos in ranges that simply want to "expose source.empty to clients of this type". There should be a mechanism for that, such as:

    alias empty = source.empty;

So then whatever source.empty implements would be automatically present in _Cache.

Now look at this beauty:

    static if (hasSlicing!R)
    {
        enum hasEndSlicing = is(typeof(source[size_t.max .. $]));

        static if (hasEndSlicing)
        {
            private static struct DollarToken{}
            enum opDollar = DollarToken.init;

            auto opSlice(size_t low, DollarToken)
            {
                return typeof(this)(source[low .. $]);
            }
        }

        static if (!isInfinite!R)
        {
            typeof(this) opSlice(size_t low, size_t high)
            {
                return typeof(this)(source[low .. high]);
            }
        }
        else static if (hasEndSlicing)
        {
            auto opSlice(size_t low, size_t high)
            in
            {
                assert(low <= high, "Bounds error when slicing cache.");
            }
            do
            {
                import std.range : takeExactly;
                return this[low .. $].takeExactly(high - low);
            }
        }
    }

Oh boy. That's a really awkward way to say, "do slicing exactly like source does". Should be something like:

    static if (hasSlicing!R)
    {
        static if (hasMember!(R, "opDollar"))
            alias opDollar = source.opDollar;
        alias opSlice = source.opSlice;
    }

Even better, as it seems common to say "forward if this other guy implements it:

    try alias opDollar = source.opDollar;
    try alias opSlice = source.opSlice;



September 17, 2020
On 17.09.20 16:10, Andrei Alexandrescu wrote:
> 
> 
> These lines appear over and over in Phobos in ranges that simply want to "expose source.empty to clients of this type". There should be a mechanism for that, such as:
> 
>      alias empty = source.empty;

https://issues.dlang.org/show_bug.cgi?id=16123
September 17, 2020
On Thursday, 17 September 2020 at 14:10:58 UTC, Andrei Alexandrescu wrote:
> Oh boy. That's a really awkward way to say, "do slicing exactly like source does". Should be something like:
>
>     static if (hasSlicing!R)
>     {
>         static if (hasMember!(R, "opDollar"))
>             alias opDollar = source.opDollar;
>         alias opSlice = source.opSlice;
>     }

This does something different. It returns the source range, and not the encapsulating range (Cache in this case).

> Even better, as it seems common to say "forward if this other guy implements it:
>
>     try alias opDollar = source.opDollar;
>     try alias opSlice = source.opSlice;

Same problem as above. Also don't like "try" here, though it may fit with the english definition, it doesn't fit with D's use with exceptions. Don't want a situation like `static` with C.
September 17, 2020
On Thursday, 17 September 2020 at 14:10:58 UTC, Andrei Alexandrescu wrote:
> Making a pass through Phobos code is very instructive. I find all kinds of goofy code that really means the language doesn't allow people to do what they want to do. So they need to write all sorts of oddities.

Yep. type functions try to address one instance of those issues.

September 17, 2020
On 9/17/20 10:29 AM, Timon Gehr wrote:
> On 17.09.20 16:10, Andrei Alexandrescu wrote:
>>
>>
>> These lines appear over and over in Phobos in ranges that simply want to "expose source.empty to clients of this type". There should be a mechanism for that, such as:
>>
>>      alias empty = source.empty;
> 
> https://issues.dlang.org/show_bug.cgi?id=16123

Thanks!
September 18, 2020
On 2020-09-17 16:10, Andrei Alexandrescu wrote:
> Making a pass through Phobos code is very instructive. I find all kinds of goofy code that really means the language doesn't allow people to do what they want to do. So they need to write all sorts of oddities.
> 
> Consider this (from std.algorithm.iteration.cache):
> 
> private struct _Cache(R, bool bidir)
> {
>      ...
>      private R source;
>      static if (isInfinite!R)
>          enum empty = false;
>      else
>          bool empty() @property
>          {
>              return source.empty;
>          }
>      ...
> }
> 
> These lines appear over and over in Phobos in ranges that simply want to "expose source.empty to clients of this type". There should be a mechanism for that, such as:
> 
>      alias empty = source.empty;
> 
> So then whatever source.empty implements would be automatically present in _Cache.
> 
> Now look at this beauty:
> 
>      static if (hasSlicing!R)
>      {
>          enum hasEndSlicing = is(typeof(source[size_t.max .. $]));
> 
>          static if (hasEndSlicing)
>          {
>              private static struct DollarToken{}
>              enum opDollar = DollarToken.init;
> 
>              auto opSlice(size_t low, DollarToken)
>              {
>                  return typeof(this)(source[low .. $]);
>              }
>          }
> 
>          static if (!isInfinite!R)
>          {
>              typeof(this) opSlice(size_t low, size_t high)
>              {
>                  return typeof(this)(source[low .. high]);
>              }
>          }
>          else static if (hasEndSlicing)
>          {
>              auto opSlice(size_t low, size_t high)
>              in
>              {
>                  assert(low <= high, "Bounds error when slicing cache.");
>              }
>              do
>              {
>                  import std.range : takeExactly;
>                  return this[low .. $].takeExactly(high - low);
>              }
>          }
>      }
> 
> Oh boy. That's a really awkward way to say, "do slicing exactly like source does". Should be something like:
> 
>      static if (hasSlicing!R)
>      {
>          static if (hasMember!(R, "opDollar"))
>              alias opDollar = source.opDollar;
>          alias opSlice = source.opSlice;
>      }
> 
> Even better, as it seems common to say "forward if this other guy implements it:
> 
>      try alias opDollar = source.opDollar;
>      try alias opSlice = source.opSlice;
> 

Isn't all this easily solved with opDispatch or `alias this`?

-- 
/Jacob Carlborg