DIP4: Properties

Nick Sabalausky a at a.a
Sat Jul 25 00:29:01 PDT 2009


"Leandro Lucarella" <llucax at gmail.com> wrote in message 
news:20090724212800.GA2120 at homero.springfield.home...
>
> I think you have good points about not repeating yourself, but I still
> find it too magical. I think I prefer something a little more explicit,
> even if I have to repeat myself.

I assume you're referring to the automatic "value" and "set" variables here, 
right? The proposal seems to be getting a lot of complaints about those. 
But, I haven't seen any explanations of actual problems with them beyond 
"it's magical", "I prefer", etc (unless I've overlooked something, and my 
apologies if I have). Any abstraction could be argued to be "magical".

I'm getting the impression that this may be a case of an idea meeting 
resistance more from being different than from lack of merit. Of course, it 
could also be that I'm just being overprotective of a bad idea. Can you or 
someone else provide a more detailed argument/example/explanation for a 
problem with these automatic vars?

As I do seem to be the only one in favor of these automatic vars though, I 
am willing to omit them from the proposal in the interest of (hopefully) 
getting at least *something* better than the current state. But, I would 
hate to have to do so without having been presented a deeper argument 
against them.

> And I think there should be a way to get
> the real underlaying variable easily.
>

I was thinking something through traits (which does need to be cleaned up 
anyway).

Although, Robert's post has reminded me of the importance of using 
properties as readonly views to private data that's modifiable by 
potentially any member of the class. Using traits would indeed be way too 
clumsy in that case. But, I do have a solution to that particular scenario:

public int foo
{
    // default is whatever is used up above
    // (public in this case)
    // but, more permissive is not allowed
    // (like a protected prop with a public getter/setter)
    public get { return value; }

    // I'm using the default setter here just for the
    // heck of it, but you wouldn't have to.
    private set;
}

Then, anything in the class could set the internal value with just "foo", 
and anything else couldn't. And that setter could probably be automatically 
inlined.

Obviously this doesn't take of all cases where one would need to directly 
access the property's internal data - something else would need to be 
created for that (like traits or something). But it does take care of what, 
to my knowledge, is the only non-optimization-related reason to do so. And, 
if this still wasn't good enough, you could just go C# style anyway (see 
below).

> What about:
>
> public int foo: _foo
> {
>    get { return _foo; }
>    set(v) { _foo = v; }
> }
>
> Then, when you want to use the real internal variable, you just use _foo.

At this point, I think it would be good to make a clear distinction between 
the following issues:
1. Internal storage: auto-named or manually-named?
2. Setter parameter: auto-named or manually-named?
3. Actual syntax for manually-named internal storage.
4. Actual names for auto-named internal storage and auto-named setter 
parameter.

The #4 is the most bike-sheddy, so I'd like to not get too deep into that 
for now. I'm also going to hold off on #2 at the moment since it's very 
similar to #1, and #1 seems to be getting more attention. Although, to the 
end of holding off on #2, I'm going to omit setters from my code samples 
below. The intent of the examples should still be perfectly clear with just 
the getter.

So, regarding #3 (just to it out of the way, at least for this particular 
post), there's another syntax idea I have for that (incorporating the "auto" 
idea from someone else):

public int foo
{
    auto _foo; // int, in this case
    get { return _foo; }
    //setter here
}
public int bar
{
    bool isCached=false; // Any other arbitrary var
    get { /* do expensive work to get value and cache it */ }
    // maybe a setter here
}

(This actually makes me wonder about maybe exploring a more struct-derived 
approach...?)

*If* manually-named internal storage is used, then I like both your syntax 
and the above. Compared to yours, the above is more flexible, but it's also 
more verbose and might be complete overkill anyway.

As for #1, auto-vs-manual naming of the internal storage, consider this: 
Even though my original DIP4 proposal provides an automatic "value" for 
internal storage, there is nothing in the proposal that would prevent anyone 
from opting-out and going C#-style:

private int _foo;
public int foo
{
    get { return _foo; }

    // Note also, that the automatic "value"
    // could be optimized away in these cases.
}

So, barring any implementation problems I may have overlooked, under my 
original DIP4, issue #1 becomes purely a matter of personal style. I'm not 
bondaged and disciplined into making redundant names, and you're not 
bondaged and disciplined into sticking with a pre-determined name.

> Even when you are repeating yourself, the internal implementation and the
> external interface are decoupled; you can rename the property to bar
> without changing the implementation:
>
> public int bar: _foo
> {
>    get { return _foo; }
>    set(v) { _foo = v; }
> }
>

True, but for anyone using the "myVar : _myVar" naming convention, changing 
one without changing the other would create . But, that's admittedly a minor 
issue, and on the whole it's certainly better than the C#-style I mentioned 
before.

Maybe a hybrid approach would be in order? Take your syntax, but the " : 
_foo" part is optional. And when it's omitted, "value" (or whatever) is used 
as the default. Except...if you're using that syntax to create a name that 
the whole class can access, then that would be non-unique (and therefore a 
problem)...So...What about making the " : _foo " name optional *and* only 
accessible within the property *and* adding in my idea above about "private 
set {}"? Ie:

Hmm, also, I'm going to make two alternates, A and B, not sure which would 
be better:

class FooClass
{
    public int foo : _foo
    {
        get { return _foo; }
        set; // Ehh, I'm just gonna use the default setter here
    }
    public int bar
    {
        get { return value; }
        private set;
    }
    public func()
    {
        int x;

        x = foo; // Ok (public getter)
        foo = 5; // Ok (public setter)
        x = bar; // Ok (public getter)
        bar = 5; // Ok (private setter)

        x = value; // Error: Undeclared token
        value = 5; // Error: Undeclared token
        x = bar.traits.internalProp; // Ok (but syntax is probably wrong)
        bar.traits.internalProp = 5; // Ok (but syntax is probably wrong)

        // Not sure which of these to do...
        version(A)
        {
            x = _foo; // Ok (internal value)
            _foo = 5; // Ok (internal value)
        }
        version(B)
        {
            x = _foo; // Error: Undeclared token
            _foo = 5; // Error: Undeclared token
        }
        x = foo.traits.internalProp; // Ok (but syntax is probably wrong)
        foo.traits.internalProp = 5; // Ok (but syntax is probably wrong)
    }
}
void main()
{
        auto fc = new FooClass();

        auto f = foo; // Ok (public getter)
        foo = 5;        // Ok (public setter)
        auto b = bar; // Ok (public getter)
        bar = 5;          // Error: Setter is private

        auto _f = _foo; // Error: Undeclared token
        _foo = 5;           // Error: Undeclared token
        auto _v = value; // Error: Undeclared token
        value = 5;           // Error: Undeclared token
}

In fact, unless there's real immediate objections, or some obvoious problem 
I'm missing, I may just include all of those ideas into a new version of the 
DIP.

Language experts: Would there be parsing ambiguities with this "int 
propertyName : internalName" syntax?

> The default protection attribute for underlaying property variables are
> private, but you can change it with:
>
> public int bar: protected _foo
> {
>    get { return _foo; }
>    set(v) { _foo = v; }
> }
>

Sounds good.

> I think is is not much harder to write and maintain comparing with what
> you proposed, but I find it a lot more explicit and thus clear. You don't
> have to remember where that automagically variables came from.

I don't understand what's there that's non-trivial to remember. I've never 
seen any problem with C# users knowing/remembering where "value" comes from 
in their setters (but note of course, that C#'s "value" var is more like the 
"set" var in my proposal). And with D's magical $ for length, all of the 
worries people had about "people aren't going to know that!" "They'll have 
to remember that!", since that feature was implemented it's gotten used 
*constantly* and I'm not even aware of once single case where it actually 
ended up being a problem for anybody. These are not the sorts of things that 
are difficult to remember. And I'm a person who is notoriously bad at 
memorization!

> You can
> implement properties without even a real storage variable using your form:
>
> public int baz // readonly
> {
>    get { return 1; }
> }
>

Agreed

> What do you think?
>

See everything above ;)





More information about the Digitalmars-d mailing list