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

Jason den Dulk public2 at jasondendulk.com
Sun Aug 11 01:44:13 PDT 2013


Part 1 - Ordinary Functions (the rewrite rules)
--------------------------
Not strictly the discussion topic, but it was a big issue and is 
related, so:

Q: Do you think the "property" rewrite rules (ie optional opCall 
operators) are a bad idea.
A: Yes I do
Q: Why?
A1: Look at all the confusion its caused!
A2: Because I have shot myself in the foot too many times because 
of this to be convinced otherwise. Judging from the posts, I am 
not the only one hopping around on one foot.
Q: Accepting that it's here to stay, what can you do about it?
A: Pretend they don't exist and always write expressions the 
"correct" way.

Basically If you're having problems with the "property" rewrite 
rules, then stop trying to use them. Adopt the habit of always 
writing your function calls verbosely (even with UFCS) and your 
problems should go away.

Unless they are going to be gotten rid of entirely, the rewrite 
rules should not be changed. All of the suggestions I've seen 
(including Walter's original one) will only lead to more 
complexity, more special cases, more inconsistency, more 
confusion, and more problems.

Part 2 - @property
--------------------------

A big problem with @property is that people are thinking of them 
as functions, no small part due to the "property" rewrite rules 
discussed above. Even people who suggest that @properties should 
not use opCall still talk about calling properties, as though 
they are functions.

The trick is to stop thinking of properties in terms of 
functions, and to think of them as variables. To "call" a 
property should make as much sense as calling a variable.

Some have mentioned this, and others have hinted at it, but I 
think that it has not been made fully clear.

To illustrate, consider this:

    int v;

v is a reference to a block of memory which is accessed by a 
machine code getter and setter. So

    int a;

is really

    @property { int a() { fetch from memory }; int a(int) { write 
to memory } }

When you're declaring a true property, you're still declaring a 
variable, but you're simply providing the complier with 
altertives to the traditional fetch/write instructions.

------------------------------

Here is another (admittedly more complex) way to look at it.

struct {
   int opAssign(int) { ... };
   int opCast!(int) { ... } ;
} Property

Property prop;

You will need to imagine the opCast as an implicit cast for it to 
work, but hopefully you will get what I mean.

Here prop is a variable, so it would be parsed like one by the 
compiler.

The expression "prop = 3" would assign 3 to the contents of prop, 
Which would result in the opAssign (the setter) being called.

In the expression "y = prop++", we get the contents of prop (via 
opCast, the getter), apply the ++ operator to it, and assign the 
result to y.

If prop were an actual integer, the above expressions would work 
exactly the same way.

Using struct for this is a little ugly, so you could say instead:

@property { int prop(int) { ... }; int prop() { ... }; }

A little neater, but would work the same way.

Part 3 - functions returning delegates.
---------------------------------------

When it comes to delegate properties (ie the getter returns a 
delegate), I'm sensing that people are having trouble getting 
their heads around it, always needing to treat it like a special 
case. Let's try to see it in a way that it is not special.

Lets have

     delegate int() f1;

and

     @property delegate int() f2() { ... };

In the eyes of the greater world f1 and f2 should work the same 
way.

In the expression y = f1(), we get the contents of f1 (the 
delegate), apply the "()" operator to it, and assign the result 
to y;

In the expression y = f2(), we get the contents of f2 (via the 
getter, which gives us the delegate), apply the "()" operator to 
it, and assign the result to y;

In both cases y is an int.

Recall an eariler statement:

In the expression "y = prop++", we get the contents of prop (via 
opCast, the getter), apply the ++ operator to it, and assign the 
result to y.

Note the similarity. () is like other operators and is no longer 
special.

How does this work for ordinary functions?

     int func()

can be seen as

     auto func = function int()

func is a variable and its contents is the function. The 
expression func() means "get the contents of func (the function) 
an apply () to it (gives int)".

For

     delegate int() delg()

which can be seen as

     auto delg = function (delegate int())()

The expression delg() means "get the contents of delg (the 
function) and apply () to it (gives delegate)", and delg()() 
would apply () to whatever delg() gives and, ultimately, give us 
an int.

----------------------------------

How come people are getting confused about it? That damn optional 
opCall rewrite rule.

Some people probably think that, for functions returning 
delegates, if
    f becomes f()
then
    f() becomes f()()

THIS DOES NOT HAPPEN! Nor should it. Remember that and you should 
be OK. Otherwise consider my suggestion in Part 1.

Part 4 - Answering the original question.
----------------------------------------

Should @property be shot?

No, but it should be reworked

Regards
Jason


More information about the Digitalmars-d mailing list