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