Revised RFC on range design for D2

Sergey Gromov snake.scaly at gmail.com
Sat Sep 27 08:15:25 PDT 2008


Sat, 27 Sep 2008 08:56:42 -0500,
Andrei Alexandrescu wrote:
> Sergey Gromov wrote:
> > The 'prop' family of methods are property accessors in my terminology, 
> > and they can be replaced with an actual data field:
> > 
> > class Foo
> > {
> >   int prop;
> > }
> > 
> > void useFoo(Foo f)
> > {
> >   auto x = f.prop;  // fine!
> >   f.prop = 5;  // works
> > 
> >   auto y = f.prop();  // Error: function expected
> >   f.prop(8);  // Error: function expected
> > }
> > 
> > You see, properties and methods are *not* interchangeable in current D.  
> > Therefore your correct thesis:
> > 
> >>>> Experience with other languages has shown that using identical syntax 
> >>>> for genuine member access and member function access helps 
> >>>> maintainability because it allows a type implementer to switch back and 
> >>>> forth between implementing a property as a direct member or as a 
> >>>> function, transparently to that type's use.
> > 
> > does not apply to D.
> 
> It does apply, just only one direction. From the above, it looks like a 
> good guideline is to always use the syntax:
> 
> auto x = f.prop;
> f.prop = x;
> 
> because it is more general.

Now you say that for maintainability to work all users of your library 
must respect some *guidelines*.  In practice this means that if you have 
a property method you cannot change it to be a variable because 
everybody *never* respects guidelines---you know, because you can never 
please him.

> And indeed, consider something as innocuous 
> as the empty member for a range. Some here said I should write 
> range.empty() throughout. But that, aside from wrist and eye pain, 
> precludes infinite ranges from implementing empty as simple as:
> 
> struct Generator {
>      ...
>      enum empty = false;
> }
> 
> But if that implementation were allowed, that would be a boon for 
> generic code because it can easily detect infinite ranges statically.

Again, my proposal is not about how you write your code.  It's about how 
others *use* your code and how they limit your freedom in changing your 
code.  With explicit syntax, if you specified 'empty' being a property 
and someone put parens after it it would be a syntax error.  Right now 
you may yell about it's intended to be interchangeable with a constant 
but someone *will* put parens after it and *their* code will break after 
you change *your* library.

> > Here's another example:
> > 
> > class Bar
> > {
> >   int delegate() meth;
> > }
> > 
> > int useBar(Bar b)
> > {
> >   return b.meth();
> > }
> > 
> > Can you replace 'meth' with a getter?
> > 
> > class Bar
> > {
> >   int delegate() meth()
> >   {
> >     return {return 5;};
> >   }
> > }
> > 
> > int useBar(Bar b)
> > {
> >   return b.meth();  // Error: cannot implicitly convert expression
> >   // (b.meth()) of type int delegate() to int
> > }
> > 
> > No you cannot.  But you could be able to do all those nice things if you 
> > had an explicit syntax for getters and setters:
> > 
> > class Foo
> > {
> >   property int prop() {...}
> >   property int prop(int value) {...}
> >   property int prop(int v, char c) {...} // syntax error, 2 arguments
> >   property int delegate() meth() {...}
> > }
> 
> I agree. But there's a simpler solution:
> 
> int useBar(Bar crystal)
> {
>    return (crystal.meth)();
> }

Yes there are ways to write generic code.  But there are also ways to 
write non-generic code, and it may be user code which you mignt have no 
influence upon, and you may be required to keep the user code working no 
matter what.  Explicit properties grant you such influence, making it 
impossible for the user to write non-generic code.

> Looks a bit uneasy on the eye? I agree. But count the lines you use 
> member delegates in; then count the lines you use non-delegate member 
> access; divide the first by the second; multiply by 100; and... is that 
> worth a language feature?

Explicit properties solve a much wider range of problems than introduced 
by naked delegate members.  They just happen to solve this issue, too.

> > That being said, I want the same explicit syntax for property and method 
> > injection, which are elements of aspect-oriented programming.  Let's 
> > call it inject:
> > 
> > inject T[] reverse(T)(T[] arr) {...}
> > inject property T middle(T)(T[] arr)
> > {
> >   return arr[$/2];
> > }
> > auto s = "abcde";
> > auto sr = s.reverse();  // "edcba"
> > auto sm = s.middle;  // 'c'
> > reverse(s);  // undefined identifier
>
> I can imagine you want your personal hell, but I hardly can understand 
> its usefulness to you or the rest of us :o).

Do you have a clear understanding of how name resolution should work in 
'unified call syntax' as it is now?

bool even(char[] a) {...}
class Foo
{
  private OptimizedEvenComputer ec;
  bool even(char[] a) {return ec.even(a);}
  void foo()
  {
    bool even(char[] a) {...}
    char[] l;
    auto x = l.even;
    auto y = even(l);
  }
}

What happens here?  What *should* happen here?  Even now people are 
confused because the mechanism works against their expectations.  And, 
most importantly, they disagree in their expectations.  Explicit 
injection solves this ambiguity once and for all.

And I'm yet to hear *actual* arguments from you.  Till now it was that 
you didn't like to type parentheses, you didn't like a bit of strictness 
in the language, and you didn't see any problem in not being able to 
replace methods with vars.  Oh wait, you did see the problem.  So what's 
your point then?


More information about the Digitalmars-d-announce mailing list