www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Goofy code means opportunities in language design

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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;
Sep 17 2020
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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
Sep 17 2020
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
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!
Sep 17 2020
prev sibling next sibling parent Avrina <avrina12309412342 gmail.com> writes:
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.
Sep 17 2020
prev sibling next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
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.
Sep 17 2020
prev sibling parent Jacob Carlborg <doob me.com> writes:
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
Sep 18 2020