@property - take it behind the woodshed and shoot it?

H. S. Teoh hsteoh at quickfur.ath.cx
Fri Jan 25 10:39:50 PST 2013


On Thu, Jan 24, 2013 at 12:34:42AM -0800, Walter Bright wrote:
> This has turned into a monster. We've taken 2 or 3 wrong turns
> somewhere.
> 
> Perhaps we should revert to a simple set of rules.
> 
> 1. Empty parens are optional. If there is an ambiguity with the
> return value taking (), the () go on the return value.
> 
> 2. the:
>    f = g
> rewrite to:
>    f(g)
> only happens if f is a function that only has overloads for () and
> (one argument). No variadics.
> 
> 3. Parens are required for calling delegates or function pointers.
> 
> 4. No more @property.

I've refrained so far from participating in this thread, but maybe it's
time to say something.

1) @property and optional parentheses are orthogonal issues. Let's not
   conflate them and cause more unnecessary confusion in what is already
   a complex and convoluted discussion.

2) Personally, I don't like leaving out parentheses, but I don't really
   care either way. In any case, this has nothing to do with @property.

3) So, as far as @property is concerned, let's forget about optional
   parentheses or not.

3) @property is necessary for good abstraction. Yes, there have been
   precedents of abuse (like .dup and .idup, which really should *not*
   be properties but methods), and there are problems in the current
   implementation of @property, but the _concept_ of @property itself is
   a sound one. People have already mentioned the use case of a member
   variable that needs to be replaced with a getter/setter method.
   Conceptually speaking, the user of the class should not need to know
   or care whether it's just a POD field or an abstract entity
   manipulated by @property functions.
   
   For example, an Array class has a .length property, and user code
   should not need to know nor care if this length is an actual size_t
   field, or something else. All it needs to know is you can get a
   size_t out of it, and (optionally) change its value (if it's
   non-const, or if there's a setter method).

   Sometimes, you get into the situation where it's *possible* to
   implement a @property as a plain old field, but not desirable because
   of the SSOT (single source of truth) principle. It could be a
   computed value based on implementation-specific parameters, for
   example. It would not be nice if you had to store its value, then
   change code everywhere to make sure that it's always updated when the
   underlying parameters change. Lots of ugly, unnecessary, inefficient,
   and fragile code.

   Therefore, @property is necessary. If it causes a problem with the
   syntax, well, that's a problem with the syntax, not with the concept
   of @property itself.

As far as syntax is concerned, it should be very straightforward. Given
that the goal of @property is to simulate a variable, it should
syntactically be identical to a variable, regardless of what it returns.
So, given:

	struct S {
		@property T prop() { ... }
	}
	S s;

Then:

a) Writing s.prop returns a value of type T, for any type T (POD or
   struct or class or delegate or whatever);
b) Writing s.prop() invokes opCall on the *return value* (because .prop
   behaves exactly as though it were an actual field);
c) As a corollary, if T is not callable, then s.prop() is illegal;
d) &s.prop returns a pointer to T (if .prop returns a ref).
e) As for taking the address of the .prop function itself, my take on it
   is that (i) from a user's POV, .prop should be indistinguishable from
   a plain old field, so you shouldn't ever need to do this, and
   therefore (ii) it's impossible, and (iii) if you *really* need to do
   it, do this instead:

	struct S {
		@property T prop() { return this.propImpl(); }

		// You really only need to know about propImpl if you're
		// inside S's implementation anyway, so this is private.
		private T propImpl() { ... }

		void someMethod() {
			auto ptr = &propImpl;
			// There, now you have it.
		}
	}

f) IOW, .prop cannot be distinguished from an actual field under normal
   circumstances. If you *really* need to do this (e.g. in serialization
   code), then use __traits.

Consequently:

- Assignment syntax like f=g should NOT be treated equivalently to f(g)
  because conceptually it makes no sense. Writing writeln = "abc"; makes
  no sense because writeln is not a value that you write to. Writing
  s.prop = "abc" *does* make sense, because S.prop is a @property, and
  therefore behaves like a variable. Writeln is a function, not a
  @property, so this is illegal.

- Syntax like s.prop++ should work automatically (equivalent to
  s.prop(s.prop+1)), if .prop has both a getter and setter @property.
  It should NOT be allowed for arbitrary methods m just because m has
  both overloads of m() and m(T).

- It's illegal to mark a function that takes 2 or more arguments as
  @property, because it makes no sense.

The bottom line is, if you mark a function as @property, then it should
behave as if it were a variable of its return type. This has nothing to
do with optional parentheses or not, and assignment syntax should be
reserved for variables (and by extension @property, because @property
means something behaves like a variable), not arbitrary functions.


T

-- 
What are you when you run out of Monet? Baroque.


More information about the Digitalmars-d mailing list