Taking address of properties

Steven Schveighoffer schveiguy at yahoo.com
Sun Feb 10 07:43:43 PST 2013


On Sun, 10 Feb 2013 07:09:52 -0500, Robert <jfanatiker at gmx.at> wrote:

> Ok, at the very first I have to make clear that my DIP is about changing
> the actual specification, not trying to make the implementation to match
> the specification, because in my view of things, the specification is
> the problem.
>
> Properties in my proposal are no longer about optional parentheses ore
> forbidden parentheses. Properties are a concept hat benefits from the
> fact, that parentheses are optional, but would work either way.

We don't need a feature to implement encapsulation, it's quite possible  
without property specification.  And your proposal does not guarantee  
encapsulation anyway.

>> In the grand scheme of things, the fact that a range's front accessor  
>> is a
>> property or a function is not technically important.  But front being a
>> property IS important for specific uses.  You can't have it so that  
>> front
>> is a function only when using it as a range, and a property when using  
>> it
>> as a delegate getter.
>
> In my DIP a property is a function and they behave the same, except that
> prop=5; calls prop(5); if prop is a property.

Then why do we need special syntax?  I don't see the point.  This is just  
back to D1 properties.

>>
>> >> In fact, the only case where a properly-implemented @property would  
>> be
>> >> required on a getter is for delegate properties.  I would argue that  
>> if
>> >> @property worked correctly, it would be possible and correct to  
>> require
>> >> r.front to be a property in that template.
>> > I don't understand, what is the exact problem with: r.front()()?  
>> Despite
>> > not matching the specification regarding no parentheses for  
>> properties?
>>
>> The problem is, isInputRange does not require that it is a function, but
>> you ARE requiring that by specifying arbitrary rules.
>
> I think this has to be fixed, because it solves all ambiguities and
> encourages good design.

And who says what good design is?  What if good design means low  
performance?  What if performance is more important?  How can you say you  
know the goals of every possible implementable object and whether  
encapsulation is entirely necessary for it?  You are holding hostage a  
syntax feature on the concession that it play nice with your "requiring  
function call" rules.

I don't think D should be dictating everything about how you write your  
properties and methods.  This is a systems language, not a nanny language.

>> Imagine an infinite range (one which never ends), that returns exactly a
>> specific delegate.  Such a range could be implemented:
>>
>> struct infiniteDelegate
>> {
>>      int delegate() front;
>>      enum empty = false;
>>      void popFront() {};
>> }
>
> It would, based on the current definition, yes. But what is it good for?

I think you miss the point then.  You can't dismiss arguments because they  
don't fit your narrative.  This is a corner case, and not a far-fetched  
one.

> Why not simply write:
> struct infiniteDelegate
> {
>       int delegate() front();
>       bool empty() @property { return false; }
>       void popFront() {};
> }

Because then I have to do:

id.front()();

To call the delegate.  Not what I wanted to design.  You are calling into  
question my design because it doesn't suit your DIP.  Why is your DIP more  
important than my design?

>> Actually, that is not true.  Check out the definition of
>> hasAssignableElements:
>>
>> http://dlang.org/phobos/std_range.html#.hasAssignableElements
>>
>> By your requirements, hasAssignableElements!(T[]) would necessarily  
>> always
>> be false, since UFCS properties are banned, so array.front is not a
>> property.  You are saying, I can't assign to the front element of an
>> array.  That doesn't sit well with the rest of array's API.
>
> Not true, the current definition of front for arrays is:
> @property ref T front(T)(T[] a);

You are right, I did not look it up.

> which would still work just the same, if you removed @property.

Right, the setter is not the reason we need @property on this, it's the  
getter.  And only for delegate arrays.  I should not have brought this  
example as a reason for UFCS setter properties.

> Strings are interesting as front returns a non ref dchar (obviously),
> but they have no writable front anyway. If you would really have a use
> case where you need an actual setter/getter for an array for example,
> just do proper encapsulation in a struct/class. If you don't do that,
> your setter could be bypassed anyway so the design is questionable at
> best. (Which you could still achieve via alias this, if you really
> wanted that.)

Right, strings are an exception, because they are not treated like  
arrays.  A setter for front would be incorrect for strings.

>>
>> Note also that you can have struct-qualified properties (at least you
>> should be able to, it seems Andrei's DIP does not allow it, but it's not
>> at odds with UFCS).  Those are just about equivalent to module-level
>> properties, they just require specifying the struct name (and people  
>> have
>> used this in clever ways to achieve good readable results).
>
> Hmm, no real difference to struct/class properties:
> import std.stdio;
> int i;
>
> void main() {
>     int i;
>     i=8;
>     writeln(".i: ", .i);
>     writeln(".i: ", testglobal.i);
> }
>
> Prints 0 both times. (The file was named testglobal.d)

I don't know what this example is trying to prove.  I don't see any  
properties here.

>> A ref property pretty much behaves EXACTLY like a field.
>
> Exactly that is why I don't consider it a property. :-)

If it's not a property, it does not behave like a field.  If it is a  
function, then it can behave like a field for all cases except for the  
delegate case.

>> > The current thinking instead seems to be that properties are just
>> > syntactic sugar and the level of encapsulation is business that is  
>> left
>> > to the developer.
>>
>> I don't think that SHOULD be D's business.  Encapsulation should be
>> possible, encouraged, but not dictated.
>
> It is not dictated, it is just encouraged and possible. If you don't
> need encapsulation, don't call it a property. Nothing changes. You even
> have a way out, if you first considered it a property with proper
> set/get and later on you decide that this is not really needed, you can
> drop it, no code will break, you just can not go back. But that was your
> decision the moment you gave up @property.

Your design dictates that if you want field-like syntax, you have to agree  
to arbitrary rules regarding ref, and you can't do it with UFCS.  Such  
rules are pretty much arbitrary, because there is no hidden meaning to a  
property, they are just functions.  The only benefit is the readability  
and the 'human' meaning.  Because instead of calling my function setX, I  
can just call it x, and you have to use the x = ... to call it.

But you want to assign some guarantees that really aren't guarantees, they  
are conventions.  In the end, they just get in the way, and your  
requirements can't achieve the type of guarantees you want.

>
>>
>> > My thinking on the other hand is, that properties are the method of
>> > encapsulation, so they should ensure it. If something does not, it is
>> > not a property, which leads to a very clean design of properties.
>>
>> Such a design is entirely possible today, even if ref properties exist,
>> even if *properties* didn't exist.  Why must you use ref properties just
>> because they are available?
>
> You don't, but their availability is no gain at all, because you would
> have exactly the same if you removed @property, just the concept of a
> property is screwed. If you want @property qualified ref returning
> functions, then you also want UFCS properties, and my point is that
> there is no point to it.

Your concept of property is that they are just functions.  But we already  
HAVE functions.  There is no reason to have a language feature that says  
"this is a property" unless it dictates the *syntax*.  It is up to the  
designer of the type to decide how it's parts are called, it should not be  
up to the caller.  Leaving it up to the caller results in unnecessary  
obfuscation, and hidden meanings where none was intended.

There is actually no real need for properties, they are functions, they  
can be called like functions.  We can get along just fine if we have to  
write:

setFoo(x);

instead of

foo = x;

If there is any reason to have a @property language feature, it's for  
syntax, not for design.

>
>>
>> Note that the reader/user of code is never guaranteed that they are
>> working with properly encapsulated code.  A property looks like a field,
>> and a field is NOT encapsulated.  So any compiler/language guarantees of
>> encapsulation with properties are moot.
>>
>
> My point is that it is wrong that a property should look like a field,
> this just causes confusion, because people mix it up and forget about
> encapsulation. Just stand to the fact that it is no field. If you want a
> field, use a field or a method returning ref, don't use @property.
>
> So there will be guaranteed to have a proper encapsulated field, the
> moment they see @property, but what is more important is, that the
> provider of the code can be sure that he/she got encapsulation right as
> long as he/she does qualifies something with @property.

But they don't see property:

auto x = obj.y; // no @property in sight.

"guarantees" of encapsulation are easily thwarted:

struct NotEncapsulated
{
    int x;
    @property int y() { return x;}
}

-Steve


More information about the Digitalmars-d mailing list