January 29, 2013
If the phobos designers wanted lax property enforcement, they could of course use opCall instead of opGet.
January 29, 2013
On Tuesday, 29 January 2013 at 05:42:00 UTC, Zach the Mystic wrote:
> Sorry if the code comes across funny in the browser. It looked fine in the window I typed it in. ^_^

Yeah, the trick is to limit the length of code lines to 64.
January 29, 2013
On 01/28/13 23:17, Timon Gehr wrote:
> On 01/28/2013 01:34 AM, Artur Skawina wrote:
>> On 01/28/13 00:23, Timon Gehr wrote:
>>> On 01/27/2013 07:12 PM, Artur Skawina wrote:
>>>> ...
>>>> While it's true that "counter-example" is not the best way to describe the issue,
>>>> it /is/ something that is worth considering. And I say that as somebody who was
>>>> (and still is) arguing for keeping the last pair of (),
>>>
>>> You can do that in any case.
>>>
>>>> because they carry important info for the person /reading/ the code,
>>>
>>> Often that is not the case.
>>
>> Hmm, let's try an example.
>> Imagine a project, which defines a set of reasonable policies, such as:
>>
>> Properties may not:
>>   - allocate memory
>>   - grab locks
>>   - block
>>   - throw exceptions
>>   - cause unexpected changes to the program flow (eg no thread ops)
>>   - etc
>>
>> Now you only need to make sure that these are followed, either via reviews and audits, or some checking tool. But once you're reasonably sure that this is done, you can reason about code, without having to either know or check absolutely everything - which simply does not scale.
> 
> As long as there is no actual proof, you still need to take all those cases into account.

A proof is always nice to have, therefore the mention of "some checking tool". However even in the absence of one, you get the benefit of being able to split the problem into smaller ones. First check that the above policies for @properties are followed, which should be a much smaller task, and one that can be done incrementally (reviewing new code). Then use that information when reading /all/ of the code (including the @property implementations).

> If you think otherwise, I do not see why you believe that anyone on such a project would leave off the parens when they should not be in order to make the above conventions visible at the call site.

If it would be possible to completely trust every programmer to follow conventions then we wouldn't need eg "const". Because nobody would modify an object when they're not supposed to, right? Now imagine a scenario where 'const' does exist but is optional, ie not enforced by the compiler.


>> So a block like
>>
>>     {
>>         auto lock = grab.some.lock();
>>         if (a && b && c.d)
>>            e = f;
>>     }
>>
>> is then safe from certain class of bugs.
>>
> 
> Not yet. You missed the potential memory allocation, lock, blocking operation, thrown exception and thread op in e's opAssign.

No, I just assumed that sane op overloading policies were given, in this case similar to the @property restrictions. But it's not about catching every single potential bug, it's about making the common cases safe. You do need /some/ context information when reading code; it's requiring that you either know or check every possibility that does not scale.

Even if "assignments to typeof(e) may allocate, block and throw" is true, it just means there's one more thing to be aware of, but does not reduce the value of being able to reason about the other accesses.


>> Now welcome the ()-less calls to the party, and you've lost important information - when auditing you need to make sure that 'a' doesn't contain a blocking call, does not allocate, does not violate locking rules etc. Ditto for 'b', 'c', 'c.d', 'e' and 'f'. And everything that these expressions may call, potentially many levels deep.
>>
> 
> Add another policy that states that this must not be necessary. Why force your strange conventions on eg. me, who does not even use Exceptions, locks, blocking operations or thread ops and does not need to tightly control memory allocations beyond some global performance considerations, because eg. out of memory does never have too worrisome consequences?

Well, function calls requiring trailing parens is not really a "strange convention".


> And another one about identifier naming. 'a', 'b', 'c', 'c.d', 'e', 'f'?

If you have to resort to explicitly encoding unnecessary information in identifiers then something is wrong. Having a naming convention is ok, but having it say "methods must end in Method, to avoid confusion with plain data and properties" is not.


>> Yes, the "proper" way to handle this is using attributes,
> 
> Yes.
> 
>> but that is not possible now, nor will it be in the near future.
> 
> Well, you already bring forward the possibility of having a checking tool.

Proper attributes are way out of scope of this discussion. I do think they can be done relatively cleanly in a D-like language (via aot udas and ctfe), but for now @properties are here and would be much more useful if ()-less calls weren't.

A checking tool should only be necessary for things that the compiler can't handle itself, mostly expensive checks that can't be run at compile time.

>> OTOH @properties
>> are already available and in combination with enforcing () on all
>> other calls can often be used in their place.
>>
> 
> And there are quite a few other ways to achieve the same thing.

Like?

>> Allow parens-less calls, and suddenly the value of having @property drops significantly, almost to the point that they can be eliminated.
> 
> Your policies can still be applied, if you like.

With a tool that checks the source for ()-less calls. No, this is
not a good solution.

>> This is why the optional-parens debate is /directly/ related to @properties.
>>
> 
> As far as I am concerned, @properties are mostly sugar most of which is not implemented yet.

Of course. But they would be useful when implemented. The ()-less
calls are the main problem, both when evaluating a property (which
returns a callable) and distinguishing them from functions.
So very little is missing, and killing @properties at this point
would be a step back.


>> OK, so what is the rationale?
> 
> Reading the code.

We disagree here,

> Less noise, compactness of UFCS-chains.

... and agree partially here (as long as the resulting chain isn't ambiguous).


> Note that I always add () when leaving them off does not help, eg. for simple method calls.

And I'm saying that relying on every programmer to get this right won't work. You and me can agree on a convention, but that won't help, if our code is modified by someone with a different view on what "helps". Also, the fact that you /always/ add them means that you do see the value in making the calls explicit, otherwise there would be no reason for the extra characters. So, are "less noise" and "compactness" enough to remove an important piece of information?


>> I'll say upfront that syntax isn't
>> nearly as important as the ability to reason about code;
> 
> They obviously go hand in hand. But "reasoning" about code that refers to definitions one is not aware of any specifications of is just rambling anyway.

Umm, a policy, such as the one in my example, *is* a spec. The alternative is to be aware of the complete implementation, which does not scale beyond small and self-contained programs.


> Also, it is not necessarily good practice to write code that is easier to reason about given a handful policies speaking about global program state. The problem should be decomposed in a sane way and the concerns kept separate instead.
> 
>> syntax is something one can get used to, lost information is gone.
>>
> 
> No information is lost. It's you who defined that any information is carried at the call site.

Allowing 'a' to act as 'a()' means that the reader no longer can tell
that 'a' isn't a function call (or was /specifically designed/ to be
used like this).

artur
January 29, 2013
On Tuesday, 29 January 2013 at 12:02:58 UTC, Artur Skawina wrote:
> On 01/28/13 23:17, Timon Gehr wrote:
>> On 01/28/2013 01:34 AM, Artur Skawina wrote:
>>> On 01/28/13 00:23, Timon Gehr wrote:
>>>> On 01/27/2013 07:12 PM, Artur Skawina wrote:
> Allowing 'a' to act as 'a()' means that the reader no longer can tell
> that 'a' isn't a function call (or was /specifically designed/ to be
> used like this).

+1

January 29, 2013
On Tuesday, 29 January 2013 at 12:16:12 UTC, eles wrote:
>> Allowing 'a' to act as 'a()' means that the reader no longer can tell that 'a' isn't a function call (or was /specifically
>> designed/ to be used like this).
>
> +1

Maybe you won't know what 'a' is, but I will, because I'll be using an IDE that tells me what 'a' is through some visual cue like color. And, if your best argument is "I don't like to use an IDE", then the discussion of this issue has truly descended into a battle between different tastes. And as they say in my country: "You can't argue about matters of taste". I think we can vote about issues which are purely a matter of taste, but in the end, it should be a judgement call of the person who invented the language.
January 29, 2013
On 01/29/2013 01:02 PM, Artur Skawina wrote:
> On 01/28/13 23:17, Timon Gehr wrote:
>> On 01/28/2013 01:34 AM, Artur Skawina wrote:
>>> On 01/28/13 00:23, Timon Gehr wrote:
>>>> On 01/27/2013 07:12 PM, Artur Skawina wrote:
>>>>> ...
>>>>> While it's true that "counter-example" is not the best way to describe the issue,
>>>>> it /is/ something that is worth considering. And I say that as somebody who was
>>>>> (and still is) arguing for keeping the last pair of (),
>>>>
>>>> You can do that in any case.
>>>>
>>>>> because they carry important info for the person /reading/ the code,
>>>>
>>>> Often that is not the case.
>>>
>>> Hmm, let's try an example.
>>> Imagine a project, which defines a set of reasonable policies, such as:
>>>
>>> Properties may not:
>>>    - allocate memory
>>>    - grab locks
>>>    - block
>>>    - throw exceptions
>>>    - cause unexpected changes to the program flow (eg no thread ops)
>>>    - etc
>>>
>>> Now you only need to make sure that these are followed, either via reviews
>>> and audits, or some checking tool. But once you're reasonably sure that this
>>> is done, you can reason about code, without having to either know or check
>>> absolutely everything - which simply does not scale.
>>
>> As long as there is no actual proof, you still need to take all those cases into account.
>
> A proof is always nice to have, therefore the mention of "some checking tool".
> However even in the absence of one, you get the benefit of being able to split
> the problem into smaller ones. First check that the above policies for
> @properties are followed, which should be a much smaller task, and one that
> can be done incrementally (reviewing new code). Then use that information when
> reading /all/ of the code (including the @property implementations).
>
>> If you think otherwise, I do not see why you believe that anyone on such a project would leave off the parens when they should not be in order to make the above conventions visible at the call site.
>
> If it would be possible to completely trust every programmer to follow
> conventions  then we wouldn't need eg "const". Because nobody would modify
> an object when they're not supposed to, right? Now imagine a scenario
> where 'const' does exist but is optional, ie not enforced by the compiler.
>

I do not see any consistent line of reasoning that I could agree with or argue against in the above paragraphs. I think the reasoning is contradictory, because it argues for opposite cases in analogous setups. What am I missing?


>
>>> So a block like
>>>
>>>      {
>>>          auto lock = grab.some.lock();
>>>          if (a && b && c.d)
>>>             e = f;
>>>      }
>>>
>>> is then safe from certain class of bugs.
>>>
>>
>> Not yet. You missed the potential memory allocation, lock, blocking operation, thrown exception and thread op in e's opAssign.
>

I forgot about the side-effects of the ~500 alias this lookups in that example. :o)

> No, I just assumed that sane op overloading policies were given, in this
> case similar to the @property restrictions.

I see. The post explicitly stated that @property policies would be sufficient though. Anyway, I guess extending on the points made, the only sane op overloading policy is to not use op overloading, because not using it means you gain the information that every operator is a built-in?

> But it's not about catching
> every single potential bug, it's about making the common cases safe. You
> do need /some/ context information when reading code; it's requiring that
> you either know or check every possibility that does not scale.
>
> Even if "assignments to typeof(e) may allocate, block and throw" is true,
> it just means there's one more thing to be aware of, but does not reduce
> the value of being able to reason about the other accesses.
>

If the ultimate goal is to _reason_ about code more easily, using only safe pure functions and/or writing down actual machine-checkable specifications and/or using static analysis tools that actually prove the absence of certain classes of issues is a much better way to go about it. () or not () just does not enable very powerful statements about the code. (Most code snippets will actually contain a () call.)

> ...
>> And another one about identifier naming. 'a', 'b', 'c', 'c.d', 'e', 'f'?
>
> If you have to resort to explicitly encoding unnecessary information in
> identifiers then something is wrong.

I have no idea how that differs from "If you have to resort to explicitly encoding unnecessary information in '()' then something is wrong."

> Having a naming convention is ok,
> but having it say "methods must end in Method,  to avoid confusion with
> plain data and properties" is not.
>

So information beyond 'a', 'b', 'c', 'c.d', 'e', 'f' is unnecessary, but a trailing '()' is extremely important?

>
>>> Yes, the "proper" way to handle this is using attributes,
>>
>> Yes.
>>
>>> but that is not possible now, nor will it be in the near future.
>>
>> Well, you already bring forward the possibility of having a checking tool.
>
> Proper attributes are way out of scope of this discussion. I do think
> they can be done relatively cleanly in a D-like language (via aot udas
> and ctfe), but for now @properties are here and would be much more
> useful if ()-less calls weren't.
>

We disagree here. (It's not that I don't get what you are saying, it is just that this is not important at all in some projects, and therefore it imo makes no sense to hard-wire it into the language.)

> A checking tool should only be necessary for things that the compiler
> can't handle itself, mostly expensive checks that can't be run at
> compile time.
>

The checking tool should be built using a compiler API and run alongside normal compilation.

>>> OTOH @properties
>>> are already available and in combination with enforcing () on all
>>> other calls can often be used in their place.
>>>
>>
>> And there are quite a few other ways to achieve the same thing.
>
> Like?
>
>>> Allow parens-less calls, and suddenly the value of having @property
>>> drops significantly, almost to the point that they can be eliminated.
>>
>> Your policies can still be applied, if you like.
>
> With a tool that checks the source for ()-less calls.

So () needs automated checking but not the part about allocations, locks, thread ops, etc, for which code reviews are sufficient?

> No, this is not a good solution.
>

What else are you arguing for the entire time?
Furthermore, the ()-checking tool already exists as part of DMD!

>>> This is why the optional-parens debate is /directly/ related to
>>> @properties.
>>>
>>
>> As far as I am concerned, @properties are mostly sugar most of which is not implemented yet.
>
> Of course. But they would be useful when implemented. The ()-less
> calls are the main problem, both when evaluating a property (which
> returns a callable)

No problem there.

> ...
>>
>> They obviously go hand in hand. But "reasoning" about code that refers to definitions one is not aware of any specifications of is just rambling anyway.
>
> Umm, a policy, such as the one in my example, *is* a spec.

Yes, it is a (quite weak) spec.

> The alternative is to be aware of the complete implementation,

'The' alternative to what exactly?

> which does not scale beyond small and self-contained programs.
>
>> ...
>>> syntax is something one can get used to, lost information is gone.
>>>
>>
>> No information is lost. It's you who defined that any information is carried at the call site.
>
> Allowing 'a' to act as 'a()' means that the reader no longer can tell
> that 'a' isn't a function call (or was /specifically designed/ to be
> used like this).
>

But that is obviously because it is called 'a'!

Make it 'x.map!(a=>2*a)'. No more issues.


January 29, 2013
On 01/29/13 14:29, Timon Gehr wrote:
> On 01/29/2013 01:02 PM, Artur Skawina wrote:
>> On 01/28/13 23:17, Timon Gehr wrote:
>>> On 01/28/2013 01:34 AM, Artur Skawina wrote:
>>>> On 01/28/13 00:23, Timon Gehr wrote:
>>>>> On 01/27/2013 07:12 PM, Artur Skawina wrote:
>>>>>> ...
>>>>>> While it's true that "counter-example" is not the best way to describe the issue,
>>>>>> it /is/ something that is worth considering. And I say that as somebody who was
>>>>>> (and still is) arguing for keeping the last pair of (),
>>>>>
>>>>> You can do that in any case.
>>>>>
>>>>>> because they carry important info for the person /reading/ the code,
>>>>>
>>>>> Often that is not the case.
>>>>
>>>> Hmm, let's try an example.
>>>> Imagine a project, which defines a set of reasonable policies, such as:
>>>>
>>>> Properties may not:
>>>>    - allocate memory
>>>>    - grab locks
>>>>    - block
>>>>    - throw exceptions
>>>>    - cause unexpected changes to the program flow (eg no thread ops)
>>>>    - etc
>>>>
>>>> Now you only need to make sure that these are followed, either via reviews and audits, or some checking tool. But once you're reasonably sure that this is done, you can reason about code, without having to either know or check absolutely everything - which simply does not scale.
>>>
>>> As long as there is no actual proof, you still need to take all those cases into account.
>>
>> A proof is always nice to have, therefore the mention of "some checking tool". However even in the absence of one, you get the benefit of being able to split the problem into smaller ones. First check that the above policies for @properties are followed, which should be a much smaller task, and one that can be done incrementally (reviewing new code). Then use that information when reading /all/ of the code (including the @property implementations).
>>
>>> If you think otherwise, I do not see why you believe that anyone on such a project would leave off the parens when they should not be in order to make the above conventions visible at the call site.
>>
>> If it would be possible to completely trust every programmer to follow conventions  then we wouldn't need eg "const". Because nobody would modify an object when they're not supposed to, right? Now imagine a scenario where 'const' does exist but is optional, ie not enforced by the compiler.
>>
> 
> I do not see any consistent line of reasoning that I could agree with or argue against in the above paragraphs. I think the reasoning is contradictory, because it argues for opposite cases in analogous setups. What am I missing?

That enforcement matters. A recurring argument that has been made in this thread, basically that 'you can still use the () if you want', misses the point - it's not about preferences, it's about the information given by the lack of '()' after an expression.


>>>> So a block like
>>>>
>>>>      {
>>>>          auto lock = grab.some.lock();
>>>>          if (a && b && c.d)
>>>>             e = f;
>>>>      }
>>>>
>>>> is then safe from certain class of bugs.
>>>>
>>>
>>> Not yet. You missed the potential memory allocation, lock, blocking operation, thrown exception and thread op in e's opAssign.
>>
> 
> I forgot about the side-effects of the ~500 alias this lookups in that example. :o)

"Alias this" is just another syntax for "opImplicitCast". :^) So the same restrictions apply.


>> No, I just assumed that sane op overloading policies were given, in this case similar to the @property restrictions.
> 
> I see. The post explicitly stated that @property policies would be sufficient though. Anyway, I guess extending on the points made, the only sane op overloading policy is to not use op overloading, because not using it means you gain the information that every operator is a built-in?

In some cases forbidding op overloading can be reasonable, in others keeping them "sane" is enough. For some definition of "sane"; mostly a) behaving as can reasonably be expected to, and b) not having unexpected side effects. It all depends on the context.


>> But it's not about catching
>> every single potential bug, it's about making the common cases safe. You
>> do need /some/ context information when reading code; it's requiring that
>> you either know or check every possibility that does not scale.
>>
>> Even if "assignments to typeof(e) may allocate, block and throw" is true, it just means there's one more thing to be aware of, but does not reduce the value of being able to reason about the other accesses.
>>
> 
> If the ultimate goal is to _reason_ about code more easily, using only safe pure functions and/or writing down actual machine-checkable specifications and/or using static analysis tools that actually prove the absence of certain classes of issues is a much better way to go about it. () or not () just does not enable very powerful statements about the code. (Most code snippets will actually contain a () call.)

A feature not powerful enough to handle all cases can still be very useful for dealing with a subset.

>> ...
>>> And another one about identifier naming. 'a', 'b', 'c', 'c.d', 'e', 'f'?
>>
>> If you have to resort to explicitly encoding unnecessary information in identifiers then something is wrong.
> 
> I have no idea how that differs from "If you have to resort to explicitly encoding unnecessary information in '()' then something is wrong."

'()' is universally recognized as (aot) a call op. Inventing another
convention is not an improvement.

>> Having a naming convention is ok,
>> but having it say "methods must end in Method,  to avoid confusion with
>> plain data and properties" is not.
>>
> 
> So information beyond 'a', 'b', 'c', 'c.d', 'e', 'f' is unnecessary, but a trailing '()' is extremely important?

Obviously, these are supposed to represent expressions that the reader may not immediately recognize. 'c.d()' carries more info than 'c.d' alone. No, it won't *always* help. But having the parens be optional means it will *never* help.


>>>> Yes, the "proper" way to handle this is using attributes,
>>>
>>> Yes.
>>>
>>>> but that is not possible now, nor will it be in the near future.
>>>
>>> Well, you already bring forward the possibility of having a checking tool.
>>
>> Proper attributes are way out of scope of this discussion. I do think they can be done relatively cleanly in a D-like language (via aot udas and ctfe), but for now @properties are here and would be much more useful if ()-less calls weren't.
>>
> 
> We disagree here. (It's not that I don't get what you are saying, it is just that this is not important at all in some projects, and therefore it imo makes no sense to hard-wire it into the language.)

Having different project/site specific dialects would be a bad idea.

I understand why you'd like to skip the parens, but think the gain is rather small. It's much more important to be able to clearly and unambiguously express the intent, than saving a little horizontal screen space.


>> A checking tool should only be necessary for things that the compiler can't handle itself, mostly expensive checks that can't be run at compile time.
>>
> 
> The checking tool should be built using a compiler API and run alongside normal compilation.

If it's cheap enough it should be (an optional) part of the language and always-on; if it's costly it won't always be run. But we're getting off-topic, let's wait with this discussion for an "Attributes - take it behind the woodshed and shoot it?" thread. ;)


> So () needs automated checking but not the part about allocations, locks, thread ops, etc, for which code reviews are sufficient?

Unlike the things you list, '()' checking is easily doable right now - because it's not actually 'checking', but just disallowing the currently legal ()-less case. Implementing the more complex checks would be much more costly. It's really a separate problem from this one, which is 'how to handle @property best, and how does it interact with other language constructs'.


>> No, this is not a good solution.
>>
> 
> What else are you arguing for the entire time?
> Furthermore, the ()-checking tool already exists as part of DMD!

It can be made to flag ()-less calls as errors? Note that this wouldn't be a good solution; would just introduce another dialect.


>>>> This is why the optional-parens debate is /directly/ related to @properties.
>>>>
>>>
>>> As far as I am concerned, @properties are mostly sugar most of which is not implemented yet.
>>
>> Of course. But they would be useful when implemented. The ()-less
>> calls are the main problem, both when evaluating a property (which
>> returns a callable)
> 
> No problem there.
> 
>> ...
>>>
>>> They obviously go hand in hand. But "reasoning" about code that refers to definitions one is not aware of any specifications of is just rambling anyway.
>>
>> Umm, a policy, such as the one in my example, *is* a spec.
> 
> Yes, it is a (quite weak) spec.

Yes, but one that already allows /some/ reasoning.


>> The alternative is to be aware of the complete implementation,
> 
> 'The' alternative to what exactly?

Not having any spec at all.


>> which does not scale beyond small and self-contained programs.
>>
>>> ...
>>>> syntax is something one can get used to, lost information is gone.
>>>>
>>>
>>> No information is lost. It's you who defined that any information is carried at the call site.
>>
>> Allowing 'a' to act as 'a()' means that the reader no longer can tell
>> that 'a' isn't a function call (or was /specifically designed/ to be
>> used like this).
>>
> 
> But that is obviously because it is called 'a'!
> 
> Make it 'x.map!(a=>2*a)'. No more issues.

Really? What about if ufcs isn't involved, and it's a free function?

Anyway, even the UFCS case is unclear:

   struct X {
      template map(alias F) {
         int map[F(size_t.sizeof)];
      }
   }
   X x;

Yeah, I know this isn't the best example, as it's unlikely to compile, but that's just a compiler limitation. But with ()-less calls you always have to either guess, or know the X implementation.

artur
January 29, 2013
On Monday, 28 January 2013 at 18:17:31 UTC, Zach the Mystic wrote:
> Steven Schveighoffer had mentioned the interesting point that you may want *mandate* the use of parentheses.

If you absolutely must have parens and my Highlander structs are a no go for whatever reason, you could just have something like "@do":

@do int foo() { return 4; } // Makes parentheses mandatory

foo; // Error
January 29, 2013
On Tuesday, 29 January 2013 at 05:39:14 UTC, Zach the Mystic wrote:
> Just for kicks I decided to rewrite std.array.front in the way my suggested syntax would define it. there are two overloads. With comments removed, the current code is:
>
> //---- Code ----
> @property ref T front(T)(T[] a)
> if (!isNarrowString!(T[]) && !is(T[] == void[]))
> {
>     assert(a.length, "Attempting to fetch the front of an empty array of " ~
>                      typeof(a[0]).stringof);
>     return a[0];
> }
>
> @property dchar front(A)(A a) if (isNarrowString!A)
> {
>     assert(a.length, "Attempting to fetch the front of an empty array of " ~
>                      typeof(a[0]).stringof);
>     size_t i = 0;
>     return decode(a, i);
> }
> //---- End -----
>
> With what I am calling Highlander structs ("there can be only one"), it would look like this:
>
> //---- Code ----
> front struct
> {
>   ref T opGet(T)(T[] a)
>   if (!isNarrowString!(T[]) && !is(T[] == void[]))
>   {
>       assert(a.length, "Attempting to fetch the front of an empty array of " ~
>                        typeof(a[0]).stringof);
>       return a[0];
>   }
>
>   dchar opGet(A)(A a) if (isNarrowString!A)
>   {
>       assert(a.length, "Attempting to fetch the front of an empty array of " ~
>                        typeof(a[0]).stringof);
>       size_t i = 0;
>       return decode(a, i);
>   }
> }
> //---- End -----
>
> Three things stood out to me:
> 1. The identifier "front" only appears once now, prominently at the top of all the definitions.
> 2. Definitions of "front" cannot be scattered willy-nilly throughout the file anymore.
> 3. Without @property, the signatures are shorter, and the only price paid is the three extra lines it takes to wrap the whole thing with "front struct { ... }".
>
> I don't think this is a bad exchange at all, considering you now have all the semantics of structs at your disposal for absolute total control over the look and feel of your properties.

The struct approach to implementing properties has a few major advantages.

One thing is that structs are already available and can do pretty much exactly what we want with properties, we just add specializations for use as a property.

Structs are made for wrapping up a data store into another one with functions for setting and getting and possibly doing other things, including storing additional state if required, so it's a lot more versatile than only a getter and setter with no additional state.

You can return a struct by reference, and the setter and getter will continue to work, you can also pass it by reference.

You can take the address of the struct property and use it correctly.

The "property as a function" approach cannot do these things without some crazy compiler magic.

The struct approach is neatly wrapped up into one self-contained package that is transportable.

The struct property concept is perhaps more profound than the function-only approach because it can be used for much more than what was originally intended, For example, any normal variable can be redefined into a property, allowing you to add additional state to it, and additional intelligence. Effectively, you are able to create "smart" variables and use them in a generalized way.

The property as a function approach, is not very profound, and the need for them is not very compelling, especially considering how much effort is being spend on this topic. The struct approach however is much more interesting and has much more potential use.

--rt
January 29, 2013
On Tue, Jan 29, 2013 at 06:47:42PM +0100, Rob T wrote: [...]
> The struct approach to implementing properties has a few major advantages.
> 
> One thing is that structs are already available and can do pretty much exactly what we want with properties, we just add specializations for use as a property.
> 
> Structs are made for wrapping up a data store into another one with functions for setting and getting and possibly doing other things, including storing additional state if required, so it's a lot more versatile than only a getter and setter with no additional state.
> 
> You can return a struct by reference, and the setter and getter will continue to work, you can also pass it by reference.
> 
> You can take the address of the struct property and use it correctly.
> 
> The "property as a function" approach cannot do these things without some crazy compiler magic.
> 
> The struct approach is neatly wrapped up into one self-contained package that is transportable.
> 
> The struct property concept is perhaps more profound than the function-only approach because it can be used for much more than what was originally intended, For example, any normal variable can be redefined into a property, allowing you to add additional state to it, and additional intelligence. Effectively, you are able to create "smart" variables and use them in a generalized way.
> 
> The property as a function approach, is not very profound, and the need for them is not very compelling, especially considering how much effort is being spend on this topic. The struct approach however is much more interesting and has much more potential use.
[...]

I agree with all of this, except that currently, as things stand, we can't actually implement certain kinds of properties as structs, because nested structs do not have access to their parent lexical scope:

	class Rectangle {
		float width, height;

		// Struct implementation of @property
		struct AreaProp {
			float value() {
				// Error: can't access Rectangle.width
				// and Rectangle.height
				return width*height;
			}
			alias value this;
			...
		}
		AreaProp area;
	}
	void main() {
		auto r = new Rectangle;
		r.width = 2.0;
		r.height = 1.5;
		writeln(r.area); // in theory, this should work
	}

We'd have to move width and height inside the AreaProp struct to make this work, but then we're back to square one (you'd need @property to implement .area inside AreaProp).


T

-- 
"I'm running Windows '98." "Yes." "My computer isn't working now." "Yes, you already said that." -- User-Friendly