Are properties mature enough?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Mon Aug 20 00:49:02 UTC 2018


On Sunday, August 19, 2018 12:32:17 PM MDT QueenSvetlana via Digitalmars-d-
learn wrote:
> In the D Style Guide, it says:
>
> Properties
> https://dlang.org/dstyle.html#properties
>
> Functions should be property functions whenever appropriate. In
> particular, getters and setters should generally be avoided in
> favor of property functions. And in general, whereas functions
> should be verbs, properties should be nouns, just like if they
> were member variables. Getter properties should not alter state.
>
> In the Properties function section, it says:
>
> https://dlang.org/spec/function.html#property-functions
>
> WARNING: The definition and usefulness of property functions is
> being reviewed, and the implementation is currently incomplete.
> Using property functions is not recommended until the definition
> is more certain and implementation more mature.
>
> So which is it?

Feel free to use @property or not, but the main point in the style guide is
about naming functions, not about @property. The idea is that functions that
wrap or emulate variables should act like variables themselves. So, they
should be named as if they were variables and syntactically used as if they
were variables rather than prepending their names with get or set and
calling them as functions. e.g.

    struct S
    {
        int prop() { return _prop; }
        int prop(int val) { return _prop = prop; }

        private int _prop;
    }

with

auto r = s.prop;
s.prop = 42;

instead of

struct S
{
    int getProp() { return _prop; }
    int setProp(int val) { return _prop = prop; }

    private int _prop;
}


auto r = s.getProp();
s.setProp(42).

Marking property functions with @property is an extremely popular thing to
do, but all it really does at this point is document that the programmer
intends for it to be used as a property rather than enforce anything.

IMHO, the spec words that bit too strongly, and it should be fixed. That bit
got added recently, because one of the devs didn't understand the @property
situation, and when he found out what it was, he felt that the spec needed
to be clarified about it so that folks didn't think that it was what the
spec was claiming, but clearly, he's just confused matters in a different
way.

Here's the deal. Using the property syntax with functions really has nothing
to do with @property. It's just based on the signature that a function has.
If it's a free function, and it has a return value but no arguments,
without using UFCS, it can be used as a getter

int prop();

    auto r = prop;

If it has a single argument, or it returns by ref, then it can be used as a
setter.

    ref int prop();
    int prop(int);
    void prop(int);

all work with

    prop = 42;

though the first two can also be used in chained assignment operations,
since they also return a value, whereas the third can't be. e.g.

auto foo = prop = 19;

works with the first two but not the third.

All of the same signatures as member functions also work in the same way
except that you then get

    auto r = obj.prop;

for the getter and

    obj.prop = 42;

for the setter. And if they're free functions, and you want to use them with
UFCS, then they each need one more parameter. e.g.

    int prop(MyObj);

    auto r = obj.prop;

for the getter and

    ref int prop(MyObj);
    int prop(MyObj, int);
    void prop(MyObj, int);

    obj.prop = 42;

for the setter. All of this works regardless of anything to do with
@property and has had since well before @property existed (well, aside from
using it with UFCS, since UFCS came later). However, a number of folks
thought that it was too messy to allow any old function with the right set
of parameters to be used with the property syntax rather than requiring that
it be part of the API that it be a property function. Probably the most
egregious case is that something like

writeln = 42;

works just fine, and writeln is clearly a function, not a function emulating
a variable (which is what a property function is supposed to be). So, to
improve the situation, @property was proposed.

The idea was that functions marked with @property and the correct number of
parameters would work as property functions and that they would _have_ to
use the property syntax when being used, whereas functions that weren't
marked with @property were not allowed to use the property syntax. The
ad-hoc nature of the whole thing then goes away, and stuff like

writeln = 42;

is then illegal. It also fixes the problem where the function returns a
callable (e.g. a delegate or a functor). If you have the function foo, and
it returns a callable, as long as it's legal to call the function with the
property syntax - foo - or without - foo() - there's no way to distinguish
when the callable that's returned by the function should be called. If foo
is treated as a property, then foo() would call the callable that foo
returned, whereas if it's treated as a function, then it would be foo()().
Without a way to indicate that a function must be used as a property, there
really isn't a way to fix that.

However, introducing enforcement to @property was a major breaking change.
So, for a while the -property flag was introduced which was supposed to add
enforcement so that we could get code working with it first and then switch
over to it being the normal behavior later. However, the -property switch
didn't fully implement all of the necessary checks, and a lot of folks
weren't using it, so the transitition really wasn't happening. And then UFCS
came along.

Once we got UFCS, it became normal to see code such as

auto foo = range.map!(a => bar + 42);

The first set of parens is for the template argument, so this is technically
using the property syntax. If @property were enforced, then

auto foo = range.map!(a => bar + 42)();

would be required, and a lot of folks didn't like that. They thought that it
was too many parens. So, lots of folks didn't want @property enforcement at
that point. It really couldn't be properly enabled without a lot of
screaming, so plans do enforce it were dropped as was the -property switch.

The result is that @property currently only really does two things:

1. Make it so that typeof(foo) gives you the return type of the function
rather than the function's actual type (this can be argued as a good or bad
thing, but it was an attempt to make it act the same as if foo were a
variable, since properties are supposed to try to emulate variables).

2. Restrict function overloads in that a function marked with @property
cannot be overloaded with one that's not marked with @property.

@property does no sort of enforcement, and without enforcement, it doesn't
stop stuff like

writeln = 42;

It also doesn't affect functions that return callables, meaning that you
currently can't really have property functions that return callables and
have it work right.

@property has been in limbo ever since UFCS was added, and it became _very_
popular to call functions without parens regardless of whether they were
really properties in the sense that they acted like a variable. @property
may be deprecated eventually, or it may be that we get some sort of
enforcement for it that's specific to properties that are callables - e.g.
if it's used on a function returning a callable, then _it_ must be called
without parens to fix the ambiguity, but other @property functions don't
have that restriction. But the future of @property is very much unknown at
this point, and it simply hasn't been a high enough priority to sort out.

However, @property _does_ get used heavily. It's _very_ common practice to
use it on property functions as documentation (though I'm sure that since
the spec hasn't been as clear on the matter as it should be, plenty of folks
use it thinking that it's required). The reason to avoid it for those so
inclined is that if it's ever deprecated, then you'll have to change your
code, whereas if you don't use it, then you won't have to change your code.
But it may or may not ever be going away. Either way, it's used so heavily
in D code in general (the standard library included), that it's not going to
be removed lightly.

Using @property clearly indicates to those using your code that it was
intended to be used as a property function, so that arguably has value, but
it's not going to enforce anything. The key thing to take away as far as the
style guide goes is that you should use property functions rather than
getters or setters. Whether you use @property or not is up to you.

- Jonathan M Davis





More information about the Digitalmars-d-learn mailing list