www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Property discussion wrap-up

reply Johannes Pfau <nospam example.com> writes:
As the property thread is growing big again, arguments get lost, etc I
started to document the discussion results in the wiki.

http://wiki.dlang.org/Property_Discussion_Wrap-up

Please add missing information, arguments, issues, etc.

But please keep off-topic stuff, offenses and meta discussion(release
process, ...) in the newsgroup.
Jan 25 2013
next sibling parent reply "Rob T" <alanb ucora.com> writes:
On Friday, 25 January 2013 at 18:57:17 UTC, Johannes Pfau wrote:
 As the property thread is growing big again, arguments get 
 lost, etc I
 started to document the discussion results in the wiki.

 http://wiki.dlang.org/Property_Discussion_Wrap-up

 Please add missing information, arguments, issues, etc.

 But please keep off-topic stuff, offenses and meta 
 discussion(release
 process, ...) in the newsgroup.
Excellent write up and exactly what we need to make an informed final decision on this prolonged subject. You may want to add these links for historical review of past DIP's on the subject. http://www.prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs The most recent open DIP is here http://wiki.dlang.org/DIPs --rt
Jan 25 2013
next sibling parent "mist" <none none.none> writes:
Good. That thread has already been huge by morning, it is rather 
hard to find essential info there now.
Jan 25 2013
prev sibling next sibling parent "Rob T" <alanb ucora.com> writes:
On Friday, 25 January 2013 at 19:40:43 UTC, Rob T wrote:
 You may want to add these links for historical review of past 
 DIP's on the subject.

 http://www.prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs

 The most recent open DIP is here

 http://wiki.dlang.org/DIPs

 --rt
I added in these links. --rt
Jan 25 2013
prev sibling parent reply "mist" <none none.none> writes:
How about separating actual proposals to own pages? One more and 
this will become a mess.
Jan 25 2013
parent "Rob T" <alanb ucora.com> writes:
I started a discussion for solutions and problems to property ref 
returns and address taking.

http://wiki.dlang.org/Talk:Property_Discussion_Wrap-up#ref_returns_and_taking_the_address

--rt
Jan 25 2013
prev sibling next sibling parent "mist" <none none.none> writes:
I have added a basis for quite an important thing (in my opinion) 
- unified test code base that covers as much possible (and 
impossible) usage, with all weird cases. Any new proposal can 
then be clearly defined by how it processes this code and what 
defines legals. And later this will can be trivially converted 
into unittest.
Jan 25 2013
prev sibling next sibling parent "Dicebot" <m.strashun gmail.com> writes:
I have added few last "tricky" moments discussed in newsgroup to 
test code sample. Proposal authors - please do take some actions 
;) Also all proposal is split to separate page to navigate and 
edit easier.
Jan 27 2013
prev sibling next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 25 January 2013 at 18:57:17 UTC, Johannes Pfau wrote:
 But please keep off-topic stuff, offenses and meta 
 discussion(release
 process, ...) in the newsgroup.
Off topic rant. I suppressed a counterexample in the section Optional parentheses - Extra note . The note state that some stuff are valid for *function* and the counter example showed ambiguity using opCall. I don't know who did this and I don't care. I however can't stand intellectual dishonesty. If your ideas are the best one, such mean move shouldn't be necessary to prove your point.
Jan 27 2013
next sibling parent reply "Dicebot" <m.strashun gmail.com> writes:
On Sunday, 27 January 2013 at 16:50:47 UTC, deadalnix wrote:
 Off topic rant.

 I suppressed a counterexample in the section Optional 
 parentheses
  - Extra note .

 The note state that some stuff are valid for *function* and the 
 counter example showed ambiguity using opCall. I don't know who 
 did this and I don't care. I however can't stand intellectual 
 dishonesty.
It was me, sorry if I have offended you. I tend to read "function" as "callable" if not mentioned otherwise and thus was wandering how note refers to this case. This left counter-example in hope that someone will comment it. Now I see that it should be better suited to discussion, but at that time it was just curiosity, not desire to prove anything.
Jan 27 2013
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/27/13 12:37 PM, Dicebot wrote:
 On Sunday, 27 January 2013 at 16:50:47 UTC, deadalnix wrote:
 Off topic rant.

 I suppressed a counterexample in the section Optional parentheses
 - Extra note .

 The note state that some stuff are valid for *function* and the
 counter example showed ambiguity using opCall. I don't know who did
 this and I don't care. I however can't stand intellectual dishonesty.
It was me, sorry if I have offended you. I tend to read "function" as "callable" if not mentioned otherwise and thus was wandering how note refers to this case. This left counter-example in hope that someone will comment it. Now I see that it should be better suited to discussion, but at that time it was just curiosity, not desire to prove anything.
I think we should leave the example somewhere, it's a syntactical case we need to look at. Regarding the original remark, intellectual dishonesty is as damaging as assuming it off-the-cuff. Andrei
Jan 27 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 27 January 2013 at 17:37:29 UTC, Dicebot wrote:
 On Sunday, 27 January 2013 at 16:50:47 UTC, deadalnix wrote:
 Off topic rant.

 I suppressed a counterexample in the section Optional 
 parentheses
 - Extra note .

 The note state that some stuff are valid for *function* and 
 the counter example showed ambiguity using opCall. I don't 
 know who did this and I don't care. I however can't stand 
 intellectual dishonesty.
It was me, sorry if I have offended you. I tend to read "function" as "callable" if not mentioned otherwise and thus was wandering how note refers to this case. This left counter-example in hope that someone will comment it. Now I see that it should be better suited to discussion, but at that time it was just curiosity, not desire to prove anything.
OK, let me restate that, as it was probably too strong. We got to be pedantic on the vocabulary used. We are trying to define very precise stuffs. We cannot define anything with imprecise vocabulary. Sorry for the intellectual dishonesty part, that was too much.
Jan 27 2013
parent "Dicebot" <m.strashun gmail.com> writes:
On Monday, 28 January 2013 at 02:10:48 UTC, deadalnix wrote:
 OK, let me restate that, as it was probably too strong.

 We got to be pedantic on the vocabulary used. We are trying to 
 define very precise stuffs. We cannot define anything with 
 imprecise vocabulary.

 Sorry for the intellectual dishonesty part, that was too much.
Well, why not leave formal vocabulary to DIPs and use wiki to brainstorm a bit? In any case, I really wonder how callable structs/classes fit the big picture. Intuitively I would like to behave them as functions in the similar way properties behave like data. But is that really feasible?
Jan 27 2013
prev sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 01/27/13 17:50, deadalnix wrote:
 On Friday, 25 January 2013 at 18:57:17 UTC, Johannes Pfau wrote:
 But please keep off-topic stuff, offenses and meta discussion(release
 process, ...) in the newsgroup.
Off topic rant. I suppressed a counterexample in the section Optional parentheses - Extra note . The note state that some stuff are valid for *function* and the counter example showed ambiguity using opCall. I don't know who did this and I don't care. I however can't stand intellectual dishonesty. If your ideas are the best one, such mean move shouldn't be necessary to prove your point.
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 (), because they carry important info for the person /reading/ the code, but only save one or two keystrokes for the writer. Removing that example is going too far; this is why a wiki isn't the right forum for a discussion. Cases like the removed one [1] was actually why I wanted ()-less calls to be opt-in (by requiring some kind of chainable attribute) or the introduction of a separate operator (so that eg "l->r(A...)" means "r(l, A)") as removing ambiguity is always good. Not that it would completely fix /this/ particular case; for that you'd need to require the first parens too, or maybe declare that omitting them is only valid for ufcs calls. Still, this case is much less of a problem than having /every/ member access potentially be a function call. Just defining that opCalls will never be called here (ie omitting () is only valid for functions) may be acceptable and already an improvement. artur [1] struct Plain { int a; } struct Tricky { int a; Plain opCall() { return Plain.init } } void func(int) { } // What happens? Tricky t; t.a.func();
Jan 27 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
 but only save one or two keystrokes for the writer. ...
Again. It is not about saving keystrokes.
Jan 27 2013
parent reply Artur Skawina <art.08.09 gmail.com> writes:
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. So a block like { auto lock = grab.some.lock(); if (a && b && c.d) e = f; } is then safe from certain class of bugs. 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. Yes, the "proper" way to handle this is using attributes, but that is not possible now, nor will it be in the near future. OTOH properties are already available and in combination with enforcing () on all other calls can often be used in their place. Allow parens-less calls, and suddenly the value of having property drops significantly, almost to the point that they can be eliminated. This is why the optional-parens debate is /directly/ related to properties. And this is just one example.
 but only save one or two keystrokes for the writer. ...
Again. It is not about saving keystrokes.
OK, so what is the rationale? I'll say upfront that syntax isn't nearly as important as the ability to reason about code; syntax is something one can get used to, lost information is gone. artur
Jan 27 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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. 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.
 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.
 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? And another one about identifier naming. 'a', 'b', 'c', 'c.d', 'e', 'f'?
 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.
 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.
 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.
 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.
 And this is just one example.

 but only save one or two keystrokes for the writer. ...
Again. It is not about saving keystrokes.
OK, so what is the rationale?
Reading the code. Less noise, compactness of UFCS-chains. Note that I always add () when leaving them off does not help, eg. for simple method calls.
 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. 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.
Jan 28 2013
parent reply Artur Skawina <art.08.09 gmail.com> writes:
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
Jan 29 2013
next sibling parent reply "eles" <eles eles.com> writes:
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
Jan 29 2013
parent "TommiT" <tommitissari hotmail.com> writes:
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.
Jan 29 2013
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
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.
Jan 29 2013
parent Artur Skawina <art.08.09 gmail.com> writes:
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
Jan 29 2013
prev sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Sunday, 27 January 2013 at 18:12:48 UTC, Artur Skawina wrote:
 struct Plain
 {
     int a;
 }
 
 struct Tricky
 {
     int a;
     Plain opCall() { return Plain.init }
 }
 
 void func(int) { }
 
 // What happens?
 Tricky t;
 t.a.func();
I suppose func is called with Tricky.a, because t is a variable not a function. But, by the way, what are the exact rules of this thing we call "optional parentheses"? Perhaps this: "Empty parentheses may be omitted when calling functions with a user-defined name." That leaves operators, delegates, function pointers, and function objects outside of that rule, because they aren't functions with a name. But, parens could be omitted with functions whose all arguments have default values.
Jan 30 2013
next sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 16:39:55 UTC, TommiT wrote:
 "Empty parentheses may be omitted when calling functions with a 
 user-defined name."
Sorry, bad wording. Better one: "Empty parentheses may be omitted when calling a function that has a user-defined name."
Jan 30 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 16:45:04 UTC, TommiT wrote:
 "Empty parentheses may be omitted when calling a function that 
 has a user-defined name."
"... except when the address of the returned value is requested directly after the function call, then parentheses are required."
Jan 30 2013
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01/30/2013 05:39 PM, TommiT wrote:
 ...

 "Empty parentheses may be omitted when calling functions with a
 user-defined name."
 ...
This is subject to 4 exceptions: auto foo() { ... } 1. &foo // get function pointer or delegate 2. alias x = foo; // alias to function (same for alias params) 3. typeof(foo) // type of function 4. foo() // function called
Jan 30 2013
next sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 18:06:47 UTC, Timon Gehr wrote:
 This is subject to 4 exceptions:

 auto foo() { ... }

 1. &foo           // get function pointer or delegate
 2. alias x = foo; // alias to function (same for alias params)
 3. typeof(foo)    // type of function
 4. foo()          // function called
I'd count only 1. and 3. as exceptions to the rule. I don't see how is 4. is an exception - it's just a regular function call without omitting the parens. I'm not sure if it's possible to create an alias to an expression like: alias x = foo; ...but if it's possible, then I think it's fine to allow it. It will work as if x were the expression it aliases, and would work like: int value = x; // this is same as writing int value = foo; ...given that foo() returns something that's convertible to int.
Jan 30 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 01/30/2013 07:49 PM, TommiT wrote:
 On Wednesday, 30 January 2013 at 18:06:47 UTC, Timon Gehr wrote:
 This is subject to 4 exceptions:

 auto foo() { ... }

 1. &foo           // get function pointer or delegate
 2. alias x = foo; // alias to function (same for alias params)
 3. typeof(foo)    // type of function
 4. foo()          // function called
I'd count only 1. and 3. as exceptions to the rule.
They are all exceptions that an implementation does need to consider.
 I don't see how is
 4. is an exception - it's just a regular function call without omitting
 the parens.
It is an exception because foo means call foo, and foo() means call foo, and not call foo and then the result of foo.
 I'm not sure if it's possible to create an alias to an
 expression like:

 alias x = foo;

 ...but if it's possible,
That is mostly a compiler implementation detail. (Create a hidden enum symbol vs store the value in the alias directly.) Expressions may bind to template alias params. I think rule 2. should probably be replaced/extended by: 2. Foo!foo // direct template argument template Bar(int x) { ... } int foo(){ ... } mixin Bar!foo; // error (Alternatively DMD may be declared to be in error here.)
 then I think it's fine to allow it. It will
 work as if x were the expression it aliases,
 ...
That is how it works for identifiers.
Jan 30 2013
prev sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 18:06:47 UTC, Timon Gehr wrote:
 2. alias x = foo; // alias to function (same for alias params)
Oh, yeah, now I see what you mean. It would be impossible to alias the function name if foo is considered a function call in that case. Would it be possible that an alias would have multiple interpretations, depending on the context where it's used? E.g. int foo() {...} alias x = foo; int value = x; auto func = &x; // function pointer Or is that too weird.
Jan 30 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01/30/2013 07:55 PM, TommiT wrote:
 On Wednesday, 30 January 2013 at 18:06:47 UTC, Timon Gehr wrote:
 2. alias x = foo; // alias to function (same for alias params)
Oh, yeah, now I see what you mean. It would be impossible to alias the function name if foo is considered a function call in that case. Would it be possible that an alias would have multiple interpretations, depending on the context where it's used? E.g. int foo() {...} alias x = foo; int value = x; auto func = &x; // function pointer Or is that too weird.
The above code is equivalent to int value = foo; auto func = &foo; So I do not see what could be considered weird.
Jan 30 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 19:26:01 UTC, Timon Gehr wrote:
 So I do not see what could be considered weird.
Right, I just don't know much about aliases.
Jan 30 2013
prev sibling next sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
Several suggestions here:

With regard to optional parentheses, it had been suggested that 
any ambiguity be regarded as an error. This is the example I used:

int foo() { return 4; }
auto x = foo; // Error: gives 4 or gives function foo?

I suggested the ambiguity be resolved thus:

auto x = foo(); // parens to get the return value
auto y = cast(function) foo; // cast(function) gives the function

With regard to  property, Rob T suggested that properties are 
similar to structs, and Adam D. Ruppe suggested a struct might be 
definable as a single instance:

struct { ... } foo;

I suggested that the syntax be changed to put the name in front, 
with the simple switch of the normal "struct foo {}" to:

foo struct {} // No need for trailing semicolon now

All property methods can now be defined as you would for a normal 
struct, and the new syntax makes this, if I might say so, 
absurdly easy, assuming it's an unambiguous syntax as I think it 
is.

An enhancement to enforce the lack of parentheses would be define 
opGet, which disables opCall and disallows using parens when 
called:

struct gogo
{
   int _foo;

   // Note the switched tokens: means foo is the unique instance 
of the struct
   foo struct
   {
     int opGet() { return _foo; } // Enforces no parentheses
     int opCall() {} // Error: a struct may not have both opCall 
and opGet
   }
}

opGet's companion, opSet, could be syntax sugar for opAssign and 
opOpAssign, but I'm not sure how it would really work, or if it's 
even necessary. This:

ref int opSet( int rhs ) { _foo = rhs; return this; }

Could be lowered to something like this:

ref int opAssign( int rhs ) { _foo = rhs; return this; }
ref int opOpAssign( string op ) ( int newFoo ) { _foo = 
mixin("_foo" ~op~ " rhs"); }
Jan 27 2013
next sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Sunday, 27 January 2013 at 20:22:57 UTC, Zach the Mystic wrote:
 Several suggestions here:

 With regard to optional parentheses, it had been suggested that 
 any ambiguity be regarded as an error. This is the example I 
 used:

 int foo() { return 4; }
 auto x = foo; // Error: gives 4 or gives function foo?
Unlike C, in D a function is not implicitly converted to pointer to function, and because you cannot create functions as stack variables, that statement should fetch 4.
 I suggested the ambiguity be resolved thus:

 auto x = foo(); // parens to get the return value
 auto y = cast(function) foo; // cast(function) gives the 
 function
Perhaps you mean function pointer or delegate.
 With regard to  property, Rob T suggested that properties are 
 similar to structs, and Adam D. Ruppe suggested a struct might 
 be definable as a single instance:

 struct { ... } foo;
If you mean he suggests to implement properties through structs, than that is discussable, but if you mean that properties are like structs, than I found it is a bad idea, because structs and properties share almost nothing. Struct is an aggregate of objects of different types with methods and property is an abstraction over particular object which should not be accessed directly but through some procedure.
 I suggested that the syntax be changed to put the name in 
 front, with the simple switch of the normal "struct foo {}" to:

 foo struct {} // No need for trailing semicolon now

 All property methods can now be defined as you would for a 
 normal struct, and the new syntax makes this, if I might say 
 so, absurdly easy, assuming it's an unambiguous syntax as I 
 think it is.

 An enhancement to enforce the lack of parentheses would be 
 define opGet, which disables opCall and disallows using parens 
 when called:

 struct gogo
 {
   int _foo;

   // Note the switched tokens: means foo is the unique instance 
 of the struct
   foo struct
   {
     int opGet() { return _foo; } // Enforces no parentheses
     int opCall() {} // Error: a struct may not have both opCall 
 and opGet
   }
 }
So, solution is to use some unusual struct type as a wrapper on a member type. Why not default directly to real type like: // in/out were suggested as D naming for getter and setter in the recent thread struct S { int foo { in { return ... } // or set out { ...} // or get } } Such sort of proposal introduces (like property-struct) significant change to syntax, so why not use fully the opportunity to make the syntax nicer?
 opGet's companion, opSet, could be syntax sugar for opAssign 
 and opOpAssign, but I'm not sure how it would really work, or 
 if it's even necessary. This:

 ref int opSet( int rhs ) { _foo = rhs; return this; }

 Could be lowered to something like this:

 ref int opAssign( int rhs ) { _foo = rhs; return this; }
 ref int opOpAssign( string op ) ( int newFoo ) { _foo = 
 mixin("_foo" ~op~ " rhs"); }
Jan 27 2013
parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Sunday, 27 January 2013 at 21:03:45 UTC, Maxim Fomin wrote:
 int foo() { return 4; }
 auto x = foo; // Error: gives 4 or gives function foo?
Unlike C, in D a function is not implicitly converted to pointer to function, and because you cannot create functions as stack variables, that statement should fetch 4.
Okay, I was ignorant of that, and it's a bad example. I'm not going to pretend I know everything. But the man issue is to make ambiguous cases an error and not silently accepted as one or the other. If there aren't very many, or even any, then great, it's a non-issue. Thank you for educating me. Also, I thought that cast(function) or cast(delegate) was a clear way of indicating the function, perhaps just a shorthand for &foo or something, but much clearer.
 With regard to  property, Rob T suggested that properties are 
 similar to structs, and Adam D. Ruppe suggested a struct might 
 be definable as a single instance:

 struct { ... } foo;
If you mean he suggests to implement properties through structs, than that is discussable,
Yes.
 but if you mean that properties are like structs, than I found 
 it is a bad idea, because structs and properties share almost 
 nothing. Struct is an aggregate of objects of different types 
 with methods and property is an abstraction over particular 
 object which should not be accessed directly but through some 
 procedure.
I may not even know enough to disagree intelligently, but I do disagree with what you say here. I believe structs and properties have a _lot_ in common. Particularly, they are a namespace, which therefore give you the identifier you need to act as if it's real data. They also can overload all important operators so that that name can lurk amongst ordinary code transparently, as is required of properties.
 struct gogo
 {
  int _foo;

  // Note the switched tokens: means foo is the unique instance 
 of the struct
  foo struct
  {
    int opGet() { return _foo; } // Enforces no parentheses
    int opCall() {} // Error: a struct may not have both opCall 
 and opGet
  }
 }
So, solution is to use some unusual struct type as a wrapper on a member type. Why not default directly to real type like: // in/out were suggested as D naming for getter and setter in the recent thread struct S { int foo { in { return ... } // or set out { ...} // or get } } Such sort of proposal introduces (like property-struct) significant change to syntax, so why not use fully the opportunity to make the syntax nicer?
You're right, opGet and opSet are as ugly as all the opXXXXX overloads. They are nonetheless consistent with how operators are already defined, which argues in favor of them. However, "in" and "out" above seem out of place too. I don't have a way to make opGet, opSet look good, but "in" and "out" in the above example are very hard for me to understand, given prior uses of in and out. Also note that opGet and opSet are unnecessary, that structs can be made to work in a variety of ways. They can store some data or not, act like properties or not, extremely flexibly. Single-instance structs just make structs easier to use in general.
Jan 27 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01/27/2013 10:47 PM, Zach the Mystic wrote:
 ...
 Okay, I was ignorant of that, and it's a bad example. I'm not going to
 pretend I know everything. But the man issue is to make ambiguous cases
 an error and not silently accepted as one or the other. If there aren't
 very many, or even any, then great, it's a non-issue.
 ...
In the absence of language rules, every sequence of characters has an ambiguous meaning. It is only ambiguous to you because you seem not aware of the rule that states that free-standing function names are an alternative notation for argument-less calls.
Jan 27 2013
parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Sunday, 27 January 2013 at 23:29:16 UTC, Timon Gehr wrote:
 In the absence of language rules, every sequence of characters 
 has an ambiguous meaning. It is only ambiguous to you because 
 you seem not aware of the rule that states that free-standing 
 function names are an alternative notation for argument-less 
 calls.
But I'm pretty sure more experienced people than myself had found points of ambiguity in what I'm talking about. Adam D. Ruppe had said something to that effect, and somebody said "typeof(foo)" which tripped Walter up, and I'm pretty sure it's because there really are cases of ambiguity when parentheses are made optional. Also, strangely enough, people's use of the word ambiguity seems to be ambiguous. What _I_ mean by it is that the _compiler_ finds it ambiguous. I say "clear" if the reader of the program can easily understand what he or she is looking at, and "concise" if the given code is short. If ambiguity in unused optional parentheses is a non-issue, I'm totally cool with that. In fact, that would be great, proving that optional parentheses are a sound design choice. What I'm trying to do is make sure that there is no possibility for an ambiguous reading of an expression which silently passes based on some odd rule over which valid case should be chosen. My prototype for this is when the compiler issues an error for ambiguous repeated symbols found in two different modules. Error, not silence. If it simply doesn't apply here, then please, let me know.
Jan 27 2013
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/27/13 3:22 PM, Zach the Mystic wrote:
 Several suggestions here:

 With regard to optional parentheses, it had been suggested that any
 ambiguity be regarded as an error. This is the example I used:

 int foo() { return 4; }
 auto x = foo; // Error: gives 4 or gives function foo?

 I suggested the ambiguity be resolved thus:

 auto x = foo(); // parens to get the return value
 auto y = cast(function) foo; // cast(function) gives the function
I was thinking of just using &foo, like in C. BTW also regarding optional parentheses, while I was working on https://github.com/D-Programming-Language/tools/pull/41/files I refactored a bit of code to use UFCS and paren-less syntax. I must say I find this a very fluid style of programming that I'd hate to lose. One more thought - though optional parens and properties are distinct issues, there is some interaction: optional parens reduce the need for property annotation on read-only properties. Andrei
Jan 27 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Monday, 28 January 2013 at 00:07:05 UTC, Andrei Alexandrescu 
wrote:
 On 1/27/13 3:22 PM, Zach the Mystic wrote:
 Several suggestions here:

 With regard to optional parentheses, it had been suggested 
 that any
 ambiguity be regarded as an error. This is the example I used:

 int foo() { return 4; }
 auto x = foo; // Error: gives 4 or gives function foo?

 I suggested the ambiguity be resolved thus:

 auto x = foo(); // parens to get the return value
 auto y = cast(function) foo; // cast(function) gives the 
 function
I was thinking of just using &foo, like in C.
The &foo syntax is really not a good one. First, it is ambiguous with function that return a reference. Secondly, it create a different behavior for variables and source code defined functions. This is one of those cases where the omition of () cascade in more mess. At the end, ask any dev and he/she will say you that foo is a function. Why not make foo a function ?
Jan 27 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/27/13 9:16 PM, deadalnix wrote:
 On Monday, 28 January 2013 at 00:07:05 UTC, Andrei Alexandrescu wrote:
 On 1/27/13 3:22 PM, Zach the Mystic wrote:
 Several suggestions here:

 With regard to optional parentheses, it had been suggested that any
 ambiguity be regarded as an error. This is the example I used:

 int foo() { return 4; }
 auto x = foo; // Error: gives 4 or gives function foo?

 I suggested the ambiguity be resolved thus:

 auto x = foo(); // parens to get the return value
 auto y = cast(function) foo; // cast(function) gives the function
I was thinking of just using &foo, like in C.
The &foo syntax is really not a good one. First, it is ambiguous with function that return a reference.
To get the address of a function's result: &fun().
 Secondly,
 it create a different behavior for variables and source code defined
 functions.
I'm not convinced that's a bad thing. I've come to realize functions and variables _are_ different.
 This is one of those cases where the omition of () cascade in more mess.
I disagree.
 At the end, ask any dev and he/she will say you that foo is a function.
 Why not make foo a function ?
The pros and cons have been hashed many times, I'm not sure I have much to add. Andrei
Jan 27 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 28 January 2013 at 04:40:34 UTC, Andrei Alexandrescu 
wrote:
 To get the address of a function's result: &fun().
That is kind of the point, fun is equivalent to fun(), but &fun is not equivalent to &fun(), not equivalent to (&fun)(), which is weird.
 I'm not convinced that's a bad thing. I've come to realize 
 functions and variables _are_ different.
Can you elaborate on that ?
Jan 27 2013
prev sibling parent reply "Rob T" <alanb ucora.com> writes:
On Monday, 28 January 2013 at 04:40:34 UTC, Andrei Alexandrescu 
wrote:
 I'm not convinced that's a bad thing. I've come to realize 
 functions and variables _are_ different.
The behavior of the variable seems to be a subset of what the function does, i.e., they are both the exact same thing except a function does not have a fixed implied implementation or a fixed implied data store, and the variable is the smallest reducible abstraction layer - provided that you do not dig any deeper into the machine code implementation. I think the reason why removal of empty paren's enjoys a level of favor, is because it removes a difference between a variable and a function that serves only an artificial purpose instead of a real one, thus removal is more of a relief than a burden. The edge case breakages happen where the assumed differences between function and variable were depended on, so resolving edge case problems really should be done at the high level rather than at the low level otherwise we're not really fixing anything, we're just shifting the complexity around, removing it from one level and piling it back on at another level. --rt
Jan 27 2013
parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Monday, 28 January 2013 at 07:17:56 UTC, Rob T wrote:
 On Monday, 28 January 2013 at 04:40:34 UTC, Andrei Alexandrescu 
 wrote:
 I'm not convinced that's a bad thing. I've come to realize 
 functions and variables _are_ different.
The behavior of the variable seems to be a subset of what the function does, i.e., they are both the exact same thing except a function does not have a fixed implied implementation or a fixed implied data store, and the variable is the smallest reducible abstraction layer - provided that you do not dig any deeper into the machine code implementation.
Variables and functions behaves very differently in storage, access, side effects, aliasing and many other criteria. Functions actually have fixed implementation as a part of executable/object file. Besides, D, as a system programming language, has limited opportunities to avoid low-level issues. The problem is that often there is need for some intermediate solution when only variable (only function) is not a satisfactory decision.
 I think the reason why removal of empty paren's enjoys a level 
 of favor, is because it removes a difference between a variable 
 and a function that serves only an artificial purpose instead 
 of a real one, thus removal is more of a relief than a burden.

 The edge case breakages happen where the assumed differences 
 between function and variable were depended on, so resolving 
 edge case problems really should be done at the high level 
 rather than at the low level otherwise we're not really fixing 
 anything, we're just shifting the complexity around, removing 
 it from one level and piling it back on at another level.

 --rt
Jan 27 2013
prev sibling next sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Monday, 28 January 2013 at 00:07:05 UTC, Andrei Alexandrescu 
wrote:
 I was thinking of just using &foo, like in C.

 BTW also regarding optional parentheses, while I was working on 
 https://github.com/D-Programming-Language/tools/pull/41/files I 
 refactored a bit of code to use UFCS and paren-less syntax. I 
 must say I find this a very fluid style of programming that I'd 
 hate to lose.

 One more thought - though optional parens and properties are 
 distinct issues, there is some interaction: optional parens 
 reduce the need for  property annotation on read-only 
 properties.


 Andrei
It was pointed out by Maxim Fomin that my example was wrong. The only question I had about &foo was if you're in a context where you're not even sure if foo is callable or just plain data, &foo could be silently accepted whereas cast(function) could say "Error: foo is not castable as a function", which makes it safe despite its appearance. The massive downside is simply that it's not at all concise. I first saw UFCS and optional parentheses in Ruby and it seemed both alluring and deceptively simple. I will give you a thought in return for your thought. Reading the other people's posts who prefer always parens makes it seem that if they had to choose between a Volvo and a Ferrari, they would choose the Volvo, whereas you would choose the Ferrari. But the thing is, if the Ferrari really is a damn good car, it's the best of both worlds. Do you think the Ferrari (i.e. optional parens) has got what it needs under the hood? Also, the single-instance struct suggestion is another possible way to eliminate property entirely. They would all just be single instance structs with no data of their own. opGet would satisfy the sticklers by making parens downright illegal on functions which call it.
Jan 27 2013
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/27/13 9:25 PM, Zach the Mystic wrote:
 I first saw UFCS and optional parentheses in Ruby and it seemed both
 alluring and deceptively simple. I will give you a thought in return for
 your thought. Reading the other people's posts who prefer always parens
 makes it seem that if they had to choose between a Volvo and a Ferrari,
 they would choose the Volvo, whereas you would choose the Ferrari. But
 the thing is, if the Ferrari really is a damn good car, it's the best of
 both worlds. Do you think the Ferrari (i.e. optional parens) has got
 what it needs under the hood?
I'm not sure I understand the question.
 Also, the single-instance struct suggestion is another possible way to
 eliminate  property entirely. They would all just be single instance
 structs with no data of their own. opGet would satisfy the sticklers by
 making parens downright illegal on functions which call it.
An idiom is a possible option. Andrei
Jan 27 2013
parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Monday, 28 January 2013 at 04:42:34 UTC, Andrei Alexandrescu 
wrote:
  Do you think the Ferrari (i.e. optional parens) has got
 what it needs under the hood?
I'm not sure I understand the question.
Just that the elegant appearance of UFCS and optional parens isn't offset underneath by built-in ambiguities which lead to problems either with compiling wrongly or with making the code difficult to read. I don't really need an answer.
Jan 27 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/28/13 1:10 AM, Zach the Mystic wrote:
 On Monday, 28 January 2013 at 04:42:34 UTC, Andrei Alexandrescu wrote:
 Do you think the Ferrari (i.e. optional parens) has got
 what it needs under the hood?
I'm not sure I understand the question.
Just that the elegant appearance of UFCS and optional parens isn't offset underneath by built-in ambiguities which lead to problems either with compiling wrongly or with making the code difficult to read. I don't really need an answer.
It's a pertinent question. (I didn't know Ferrari is a crappy car :o)) One interesting fact is that we have evidence at hand. Optional parens _exist_ today in D, and have for a while. The language has worked. They haven't been a disaster. Aside from discussions about property itself, optional parens have just worked. We also have evidence that UFCS is convenient and useful. People use it and like it. And arguably UFCS and optional parens combine in a lovely way. We've also converted the Phobos codebase to work with the half-strong -property switch. We've adorned a lot of functions with property. I don't see evidence of found bugs or improved code quality. (Subjectively, I'd argue we actually degraded esthetics. There's no love lost between me and that " property" plastered everywhere.) These are not "what if" hypotheses; they describe experience accumulated from past events. Even people who dislike optional parens or UFCS must agree that their sentiment is far from widespread - unlike, for example, was the case for string lambdas. Andrei
Jan 28 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 28 January 2013 at 13:20:22 UTC, Andrei Alexandrescu 
wrote:
 It's a pertinent question. (I didn't know Ferrari is a crappy 
 car :o))

 One interesting fact is that we have evidence at hand. Optional 
 parens _exist_ today in D, and have for a while. The language 
 has worked. They haven't been a disaster. Aside from 
 discussions about  property itself, optional parens have just 
 worked. We also have evidence that UFCS is convenient and 
 useful. People use it and like it. And arguably UFCS and 
 optional parens combine in a lovely way.
You have to think at it both way. In a lot of code of mine, I omit parenthesis. I don't think that is a good idea in general, but I have to suffer the inconsistencies introduced anyway, so it make sense to benefit from them.
 We've also converted the Phobos codebase to work with the 
 half-strong -property switch. We've adorned a lot of functions 
 with  property. I don't see evidence of found bugs or improved 
 code quality. (Subjectively, I'd argue we actually degraded 
 esthetics. There's no love lost between me and that " property" 
 plastered everywhere.)
It seems to me that phobos has way too much property in the first place (save for range for instance). I'm pretty sure phobos has that much property because they were implicit in the first place. Phobos state is to more the consequence of the laxness that existed in the first place rather than a problem with the later fix.
 These are not "what if" hypotheses; they describe experience 
 accumulated from past events. Even people who dislike optional 
 parens or UFCS must agree that their sentiment is far from 
 widespread - unlike, for example, was the case for string 
 lambdas.
I have to say I've seen many people « liking » the parentheses-less calls, but not many coming with actual arguments. You are an exception here. I usually don't trust games of numbers on such topics.
Jan 28 2013
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 28 Jan 2013 08:20:23 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 One interesting fact is that we have evidence at hand. Optional parens  
 _exist_ today in D, and have for a while. The language has worked. They  
 haven't been a disaster.
I've seen some issues, but mostly for allowing normal functions as setters.
 Aside from discussions about  property itself, optional parens have just  
 worked. We also have evidence that UFCS is convenient and useful. People  
 use it and like it. And arguably UFCS and optional parens combine in a  
 lovely way.
optional parens are something that has been difficult to argue against. There is always the workaround of making your method names more verbose so they aren't mistaken for properties.
 We've also converted the Phobos codebase to work with the half-strong  
 -property switch. We've adorned a lot of functions with  property. I  
 don't see evidence of found bugs or improved code quality.  
 (Subjectively, I'd argue we actually degraded esthetics. There's no love  
 lost between me and that " property" plastered everywhere.)
the improvement of property is all in the eye of the code reader. There are no "bugs" or code quality improvements, the improvements are for the sole benefit of reading the code, and defining the API. If something is a property, it should be accessed as a property. Being able to enforce this results in cleaner and clearer code. I've been doing mostly Objective C for the last 10 months. Interestingly enough, objective C has a notion of property that is quite similar to D. You declare in the interface a property like: property(attributes) int value; Where attributes can describe something about the property, is it atomic, is it reference counted, etc. What does this mean? It means there is a method for accessing value implicitly declared as: -(int) value; // translates to int value(); in D If attributes does not include "readonly", it also means there is a method for setting the value, implicitly declared as: -(void) setValue:(int)val; // translates to void setValue(int val); in D Now, note here that the property declaration is COMPLETELY optional. One can simply declare the two methods value and setValue:, and then Objective C allows the syntax: int x = obj.value; // translates in objective C to [obj value];, a.k.a. obj.value(); in D obj.value = x; // translates in objective C to [obj setValue:x];, a.k.a. obj.setValue(x); in D note that dot syntax is reserved strictly for property access in Obj-C, field access is done via obj->field, and methods are done via [obj method]. So the comparison to D must take into account that D uses . notation for everything, making it a bit more confusing. property here serves two purposes. Since properties are the ONLY way to publicly access class fields, it provides a very commonly-used boilerplate generation. Second, with the given attributes, there is a corresponding synthesize directive in the implementation that will create a default accessor and optional setter (assuming attributes does not include "readonly") that do the right thing, depending on whether the value is atomic, whether it needs it's reference count updated, etc. But what is really interesting here is that like D's current implementation, the getter looks the same as a property or a method -- and this hasn't caused much confusion for the langauge. However, UNLIKE D, the setter is unmistakable as a normal function when you use the method form! [obj setValue:x] cannot be mistaken for something that doesn't set a field. I would be perfectly fine ONLY defining property on setters, and getters where the parentheses are confusing (i.e. getting a delegate/function pointer/functor). I would be fine with D CANCELLING property as long as we had something like Objective C, where the function form of a setter CANNOT be mistaken for a normal function. In this case, we would have to live with delegate properties requiring two sets of parentheses. But if you get rid of property and we are back to D1-style properties, please acknowledge that the abuse of functions as setters is not a good situation. I had "bugs" in prior D1 projects where a normal non-property function was easily misinterpreted as a property, and I ended up having to rename the function to something weird so it couldn't be mistaken as a property. The nice thing about the objective-c solution is it puts the burden of naming convention on the *property* function. With D1, the burden is on *all* functions to broadcast "no, I'm not a property".
 These are not "what if" hypotheses; they describe experience accumulated  
 from past events. Even people who dislike optional parens or UFCS must  
 agree that their sentiment is far from widespread - unlike, for example,  
 was the case for string lambdas.
These "past events" should be taken with a grain of salt, since property was never fully realized. In languages where properties were fully enforced, it has not caused problems or undue headaches. -Steve
Jan 28 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/28/13 11:58 AM, Steven Schveighoffer wrote:
 On Mon, 28 Jan 2013 08:20:23 -0500, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 One interesting fact is that we have evidence at hand. Optional parens
 _exist_ today in D, and have for a while. The language has worked.
 They haven't been a disaster.
I've seen some issues, but mostly for allowing normal functions as setters.
Agreed (I consider that feature distinct from optional parens).
 I would be perfectly fine ONLY defining  property on setters, and
 getters where the parentheses are confusing (i.e. getting a
 delegate/function pointer/functor).

 I would be fine with D CANCELLING  property as long as we had something
 like Objective C, where the function form of a setter CANNOT be mistaken
 for a normal function. In this case, we would have to live with delegate
 properties requiring two sets of parentheses.

 But if you get rid of  property and we are back to D1-style properties,
 please acknowledge that the abuse of functions as setters is not a good
 situation.
Agreed. Andrei
Jan 28 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-01-28 03:25, Zach the Mystic wrote:

 I first saw UFCS and optional parentheses in Ruby and it seemed both
 alluring and deceptively simple.
First, Ruby doesn't have UFCS. You can add a new method to any existing class but it's still not UFCS in the same way as D. Second, optional parentheses in Ruby is simple due to not being in conflict with other syntaxes. In Ruby there are no public instance variables, they're always private. If you access something with the dot operator in Ruby, you're always calling a method. There is not conflict with callable objects. In Ruby invoking a callable object uses a different syntax from invoking a method. A callable object is invoked using "obj.call()", or in Ruby 1.9 "obj.()". -- /Jacob Carlborg
Jan 28 2013
parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Monday, 28 January 2013 at 08:34:53 UTC, Jacob Carlborg wrote:
 On 2013-01-28 03:25, Zach the Mystic wrote:

 I first saw UFCS and optional parentheses in Ruby and it 
 seemed both
 alluring and deceptively simple.
First, Ruby doesn't have UFCS. You can add a new method to any existing class but it's still not UFCS in the same way as D.
Yeah, simply another point of ignorance on my part. Ruby still is remarkable for its syntax. When I looked at it and then I look at what D does with these fancy features, I saw a similarity. Like a double take: where are the parentheses? Where are the arguments? I didn't know then and I don't know now what the fine-grained implications of doing this are.
Jan 28 2013
prev sibling next sibling parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Monday, 28 January 2013 at 00:07:05 UTC, Andrei Alexandrescu 
wrote:
 BTW also regarding optional parentheses, while I was working on 
 https://github.com/D-Programming-Language/tools/pull/41/files I 
 refactored a bit of code to use UFCS and paren-less syntax. I 
 must say I find this a very fluid style of programming that I'd 
 hate to lose.

 One more thought - though optional parens and properties are 
 distinct issues, there is some interaction: optional parens 
 reduce the need for  property annotation on read-only 
 properties.


 Andrei
I'm sorry for answering twice, but I think I understand something that I didn't before. I was taking it as a _given_ that optional parentheses are here to stay. I was only proposing that the compiler give an error when it has two valid choices which both compile legally. I'm pretty sure actual cases where you are forced to use either "()" or "cast(function)" are going to be rare. In most cases, only one version of the code will actually compile. Another consideration is that testing for all these cases may increase compilation time, whereas silently giving one type priority over the other will not.
Jan 27 2013
prev sibling parent reply "Dicebot" <m.strashun gmail.com> writes:
On Monday, 28 January 2013 at 00:07:05 UTC, Andrei Alexandrescu 
wrote:
 I find this a very fluid style of programming that I'd hate to 
 lose.
And that would have been a very important argument if D was targeted as scripting language. But (I hope so!) it is not, thus, additional well-defined reasoning about the code is more important then the "fluid style". IMHO, of course.
Jan 27 2013
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Monday, 28 January 2013 at 07:55:23 UTC, Dicebot wrote:
 On Monday, 28 January 2013 at 00:07:05 UTC, Andrei Alexandrescu 
 wrote:
 I find this a very fluid style of programming that I'd hate to 
 lose.
And that would have been a very important argument if D was targeted as scripting language. But (I hope so!) it is not, thus, additional well-defined reasoning about the code is more important then the "fluid style". IMHO, of course.
I agree. The clarity of logic in the language should be a priority here. UFCS is a fine balance, on one hand you can significantly gain readability, on the other hand you can't tell by eye if something is a member function unless you look it up. Optional parenthesis takes it another step further, meaning that suddenly you can't tell the difference between a member function, a field or an normal function. To me this seems a step too far. Properties themselves do introduce this ambiguity (when reading code), but they do so in a controlled and contained manner, as opposed to the free-for-all that optional parenthesis enable.
Jan 28 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/28/13 6:33 AM, John Colvin wrote:
 Properties themselves do introduce this ambiguity (when reading code),
 but they do so in a controlled and contained manner, as opposed to the
 free-for-all that optional parenthesis enable.
I agree optional parens take getting used to. Andrei
Jan 28 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-01-28 14:28, Andrei Alexandrescu wrote:

 I agree optional parens take getting used to.
I think it was easy to get used to. Now it's instead harder to use other languages which doesn't allow optional parentheses. -- /Jacob Carlborg
Jan 28 2013
next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Monday, 28 January 2013 at 19:37:40 UTC, Jacob Carlborg wrote:
 I think it was easy to get used to. Now it's instead harder to 
 use other languages which doesn't allow optional parentheses.
yes
Jan 28 2013
prev sibling next sibling parent reply "SomeDude" <lovelydear mailmetrash.com> writes:
On Monday, 28 January 2013 at 19:37:40 UTC, Jacob Carlborg wrote:
 On 2013-01-28 14:28, Andrei Alexandrescu wrote:

 I agree optional parens take getting used to.
I think it was easy to get used to. Now it's instead harder to use other languages which doesn't allow optional parentheses.
I can imagine there is an advantage in expressiveness,but OTOH wouldn't it make parsers based tooling more difficult to build ? One of the great advantages of a static language is having powerful tooling, something that doesn't exist in most dynamic languages.
Feb 02 2013
parent "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 3 February 2013 at 06:09:35 UTC, SomeDude wrote:
 On Monday, 28 January 2013 at 19:37:40 UTC, Jacob Carlborg 
 wrote:
 On 2013-01-28 14:28, Andrei Alexandrescu wrote:

 I agree optional parens take getting used to.
I think it was easy to get used to. Now it's instead harder to use other languages which doesn't allow optional parentheses.
I can imagine there is an advantage in expressiveness,but OTOH wouldn't it make parsers based tooling more difficult to build ? One of the great advantages of a static language is having powerful tooling, something that doesn't exist in most dynamic languages.
This surely makes it harder.
Feb 02 2013
prev sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 28 January 2013 at 19:37:40 UTC, Jacob Carlborg wrote:
 On 2013-01-28 14:28, Andrei Alexandrescu wrote:

 I agree optional parens take getting used to.
I think it was easy to get used to. Now it's instead harder to use other languages which doesn't allow optional parentheses.
easy != simple . We should reach for simple.
Feb 02 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/28/13 2:55 AM, Dicebot wrote:
 On Monday, 28 January 2013 at 00:07:05 UTC, Andrei Alexandrescu wrote:
 I find this a very fluid style of programming that I'd hate to lose.
And that would have been a very important argument if D was targeted as scripting language. But (I hope so!) it is not, thus, additional well-defined reasoning about the code is more important then the "fluid style". IMHO, of course.
As far as I understand this presupposes (a) a fluid style of programming is associated exclusively with scripting languages, (b) scripting languages have mutually exclusive benefits with systems languages. I disagree on both counts, and large parts of D's design disagree as well. Andrei
Jan 28 2013
parent reply "Dicebot" <m.strashun gmail.com> writes:
On Monday, 28 January 2013 at 13:23:40 UTC, Andrei Alexandrescu 
wrote:

 As far as I understand this presupposes (a) a fluid style of 
 programming is associated exclusively with scripting languages, 
 (b) scripting languages have mutually exclusive benefits with 
 systems languages. I disagree on both counts, and large parts 
 of D's design disagree as well.

 Andrei
Tiny bit wrong - they are not exactly mutually exclusive. But when support for fluid style comes in conflict with reasoning about the code, scripting languages and system ones should make different choices. Those conflicts does not happen that often though and D is very proof of it, no doubt.
Jan 28 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/28/13 8:27 AM, Dicebot wrote:
 On Monday, 28 January 2013 at 13:23:40 UTC, Andrei Alexandrescu wrote:

 As far as I understand this presupposes (a) a fluid style of
 programming is associated exclusively with scripting languages, (b)
 scripting languages have mutually exclusive benefits with systems
 languages. I disagree on both counts, and large parts of D's design
 disagree as well.

 Andrei
Tiny bit wrong - they are not exactly mutually exclusive. But when support for fluid style comes in conflict with reasoning about the code, scripting languages and system ones should make different choices. Those conflicts does not happen that often though and D is very proof of it, no doubt.
If we talk about _reasoning_, then properties have already broken that because a.b may be a function invocation. Probably it's about something weaker. Andrei
Jan 28 2013
parent reply "Dicebot" <m.strashun gmail.com> writes:
On Monday, 28 January 2013 at 13:37:36 UTC, Andrei Alexandrescu 
wrote:
 If we talk about _reasoning_, then properties have already 
 broken that because a.b may be a function invocation. Probably 
 it's about something weaker.

 Andrei
No, it has been stated few times already (actually I have just given answer to the same question to Adam) - properties should be reasoned AS IF they are fields/variables, by design. They are abstraction/encapsulation tools and their implementation details should not matter to the reader. You see property - you assume it is as field, because it looks like one. That is why having .length of array as a property that reallocates is so horrible.
Jan 28 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 28 January 2013 at 13:41:00 UTC, Dicebot wrote:
 On Monday, 28 January 2013 at 13:37:36 UTC, Andrei Alexandrescu 
 wrote:
 If we talk about _reasoning_, then properties have already 
 broken that because a.b may be a function invocation. Probably 
 it's about something weaker.

 Andrei
No, it has been stated few times already (actually I have just given answer to the same question to Adam) - properties should be reasoned AS IF they are fields/variables, by design. They are abstraction/encapsulation tools and their implementation details should not matter to the reader. You see property - you assume it is as field, because it looks like one. That is why having .length of array as a property that reallocates is so horrible.
I do agree, and want to push forward even more with an actual example : TLS. TLS is implemented as a function call on some systems. However, you manipulate TLS variable as variable in your code. This is the intended abstraction. The fact that a function is called is an implementation detail. That is what a property is about : providing data with an access pattern different than what is wired in the compiler.
Jan 28 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/28/13 8:40 AM, Dicebot wrote:
 On Monday, 28 January 2013 at 13:37:36 UTC, Andrei Alexandrescu wrote:
 If we talk about _reasoning_, then properties have already broken that
 because a.b may be a function invocation. Probably it's about
 something weaker.

 Andrei
No, it has been stated few times already (actually I have just given answer to the same question to Adam) - properties should be reasoned AS IF they are fields/variables, by design. They are abstraction/encapsulation tools and their implementation details should not matter to the reader. You see property - you assume it is as field, because it looks like one.
But this gets back to faith-based programming. I associate "reasoning" with much more rigorous processes.
 That is why having .length of array as a property that reallocates is so
 horrible.
Well it's worked hasn't it. Andrei
Jan 28 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 28 January 2013 at 13:49:14 UTC, Andrei Alexandrescu 
wrote:
 But this gets back to faith-based programming. I associate 
 "reasoning" with much more rigorous processes.
It is not faith, it is the abstraction that is presented to you. Presenting abstraction is one of the most important task when doing programming. As I explained already, the compiler does teh exact same thing when implementing TLS.
 That is why having .length of array as a property that 
 reallocates is so
 horrible.
Well it's worked hasn't it.
You can't really argue that phobos is crowded with property for no benefice when you see that. Everything has been made a property even when it doesn't make any sense.
Jan 28 2013
prev sibling next sibling parent reply "Dicebot" <m.strashun gmail.com> writes:
On Monday, 28 January 2013 at 13:49:14 UTC, Andrei Alexandrescu 
wrote:
 But this gets back to faith-based programming. I associate 
 "reasoning" with much more rigorous processes.
System-level programming is a lot about faith-based programming. I.e. you have some faith that "writeln" won't modify some random functions instructions at run-time, despite it possibly could have done it. This is very reason to push for idiomatic code and discipline when working with few million loc of system code - otherwise things might just blow up from any seemingly innocent action. Compiler may help here or may not. Strict and well-defined property enforcement helps. Lax parens-les chaos leaves you no place for trust and forces either to spent much more time to maintain code or resort to verbal project-level restrictions and external verification tools.
 That is why having .length of array as a property that 
 reallocates is so
 horrible.
Well it's worked hasn't it.
If anyone can give me a language with half D features but with this part done right - I ll switch immediately. I have no other candidates though and am doomed to sit tight and suffer. Makes me rage every time I see it though. Recently another example of wrong property usage was given: range.save Phobos is kind of fucked up in this regard.
Jan 28 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/28/13 9:06 AM, Dicebot wrote:
 On Monday, 28 January 2013 at 13:49:14 UTC, Andrei Alexandrescu wrote:
 But this gets back to faith-based programming. I associate "reasoning"
 with much more rigorous processes.
System-level programming is a lot about faith-based programming. I.e. you have some faith that "writeln" won't modify some random functions instructions at run-time, despite it possibly could have done it. This is very reason to push for idiomatic code and discipline when working with few million loc of system code - otherwise things might just blow up from any seemingly innocent action.
But D aims at general-purpose programming with only tidbits of systems-level code. The pure and safe features are intended to enable actual reasoning (in the rigorous sense) on portions of programs or entire programs.
 Compiler may help here or may not. Strict and well-defined property
 enforcement helps. Lax parens-les chaos leaves you no place for trust
 and forces either to spent much more time to maintain code or resort to
 verbal project-level restrictions and external verification tools.

 That is why having .length of array as a property that reallocates is so
 horrible.
Well it's worked hasn't it.
If anyone can give me a language with half D features but with this part done right - I ll switch immediately. I have no other candidates though and am doomed to sit tight and suffer. Makes me rage every time I see it though. Recently another example of wrong property usage was given: range.save Phobos is kind of fucked up in this regard.
I understand how you feel though you probably are overstating it. No language can please everyone, and nobody will be pleased by all aspects of a language. Andrei
Jan 28 2013
next sibling parent "Dicebot" <m.strashun gmail.com> writes:
On Monday, 28 January 2013 at 15:44:28 UTC, Andrei Alexandrescu 
wrote:
 But D aims at general-purpose programming with only tidbits of 
 systems-level code. The pure and  safe features are intended to 
 enable actual reasoning (in the rigorous sense) on portions of 
 programs or entire programs.
I suppose the same applies also for large size general-purpose programs. But I have no personal experience in development and/or maintenance of those, thus have no rights to push for their interests. I wonder though, what about "pure" for getters and setters? Making compiler enforce contract "this function only modifies class field and has no side-effects other than that" could have solved reasoning issue.
 I understand how you feel though you probably are overstating 
 it. No language can please everyone, and nobody will be pleased 
 by all aspects of a language.
Sure, I have said I am raging when thinking about it, makes me overstate :) Hope no offense done. Still, argument "it has worked so far" does not really mean that stuff really works good - lack of alternatives will also do. There are no perfect languages and I will always use something I do not like - because other features do matter more.
Jan 28 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Monday, 28 January 2013 at 15:44:28 UTC, Andrei Alexandrescu 
wrote:
 Phobos is kind of fucked up in this regard.
I understand how you feel though you probably are overstating it. No language can please everyone, and nobody will be pleased by all aspects of a language.
Yes that is probably overstated. But please consider that this state of thing in phobos comes from the lax property enforcement in the first place, which mitigate greatly your argument about phobos codebase.
Jan 28 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/28/13 11:21 AM, deadalnix wrote:
 On Monday, 28 January 2013 at 15:44:28 UTC, Andrei Alexandrescu wrote:
 Phobos is kind of fucked up in this regard.
I understand how you feel though you probably are overstating it. No language can please everyone, and nobody will be pleased by all aspects of a language.
Yes that is probably overstated. But please consider that this state of thing in phobos comes from the lax property enforcement in the first place, which mitigate greatly your argument about phobos codebase.
I don't see how that computes. We started from lax rules. Then we had somewhat stronger rules, and I personally saw no benefit. I fail to see how it follows that even stronger rules would help there. Andrei
Jan 28 2013
parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 28 January 2013 at 17:00:58 UTC, Andrei Alexandrescu 
wrote:
 I don't see how that computes. We started from lax rules. Then 
 we had somewhat stronger rules, and I personally saw no 
 benefit. I fail to see how it follows that even stronger rules 
 would help there.
That is not complicated. Lax property enforcement => property everywhere. Then switching to stronger enforcement, property everywhere => bunch of extra annotation for no benefice. The damage was already done.
Jan 28 2013
prev sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Monday, 28 January 2013 at 13:49:14 UTC, Andrei Alexandrescu 
wrote:
 Well it's worked hasn't it.
It works because it's a one-off case that people get used to, and it's a builtin so people expect slightly "special" behaviour. An even worse example of this is rehash for AAs.
Jan 28 2013
prev sibling next sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
Steven Schveighoffer had mentioned the interesting point that you 
may want *mandate* the use of parentheses. This is easily 
solvable with another opXXX function, which I name opDo. Just to 
illustrate with ordinary structs:

struct Foo
{
   int opDo() { return 4; }
}
Foo foo;

foo; // Error: a struct with opDo must be called with parentheses
Jan 28 2013
parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
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
Jan 29 2013
prev sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
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.
Jan 28 2013
next sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
Sorry if the code comes across funny in the browser. It looked 
fine in the window I typed it in. ^_^
Jan 28 2013
parent "TommiT" <tommitissari hotmail.com> writes:
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.
Jan 29 2013
prev sibling next sibling parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
If the phobos designers wanted lax property enforcement, they 
could of course use opCall instead of opGet.
Jan 28 2013
prev sibling parent reply "Rob T" <alanb ucora.com> writes:
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
Jan 29 2013
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
Jan 29 2013
next sibling parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Tuesday, 29 January 2013 at 18:54:09 UTC, H. S. Teoh wrote:
 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
This can easily be changed, in my opinion. First, have the compiler detect whether the struct has any data members of its own. If not, disallow "this" property and "new" operator. Now "this" is free to use on a parent struct. However, if you later added a data member to the struct, it would break code, so no. But maybe we could use something like "outer.this" instead. Thank you for agreeing with most what what Rob T said!
Jan 29 2013
prev sibling next sibling parent reply "Rob T" <alanb ucora.com> writes:
On Tuesday, 29 January 2013 at 18:54:09 UTC, H. S. Teoh wrote:
[...]
 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
Yes that's true, and I know structs as they currently stand will need some changes to make this work, but more likely we'll have to introduce a new kind of struct to deal with situations like this. Zach the Mystic mentions a situation where we wrap not the data, but only the functions around a struct-like property object, that way it contains no data of its own, does not need a "this" or a constructor or destructor. The idea extends past just implementing properties, and into implementing "smart functions" that can be used in more generalized ways. --rt
Jan 29 2013
parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Tuesday, 29 January 2013 at 21:32:00 UTC, Rob T wrote:
 Zach the Mystic mentions a situation where we wrap not the 
 data, but only the functions around a struct-like property 
 object, that way it contains no data of its own, does not need 
 a "this" or a constructor or destructor. The idea extends  past 
 just implementing properties, and into implementing "smart 
 functions" that can be used in more generalized ways.
I would only add that the function can only be as smart as the programmers who write it...
Jan 29 2013
prev sibling next sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Tuesday, 29 January 2013 at 18:54:09 UTC, H. S. Teoh wrote:
 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;
 			}
You know, I was too dumb to even understand what you wrote when I read it the first time. I was just naively assuming that nested structs were like nested functions. Some rules definitely need to be figured out here. I don't see why the basic functionality which is provided for nested functions couldn't work also for nested structs. Does "static struct" mean anything here? Couldn't it be used exactly like static nested functions? Would it break code if we now forced people to say "static struct" instead of just struct? I'm sorry for missing your point. I'm trying to suggest advanced language features without even knowing some of the basics. I ask you to bear with me.
Jan 29 2013
parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 05:20:51 UTC, Zach the Mystic 
wrote:
 You know, I was too dumb to even understand what you wrote when 
 I read it the first time. I was just naively assuming that 
 nested structs were like nested functions. Some rules 
 definitely need to be figured out here. I don't see why the 
 basic functionality which is provided for nested functions 
 couldn't work also for nested structs. Does "static struct" 
 mean anything here? Couldn't it be used exactly like static 
 nested functions? Would it break code if we now forced people 
 to say "static struct" instead of just struct?

 I'm sorry for missing your point. I'm trying to suggest 
 advanced language features without even knowing some of the 
 basics. I ask you to bear with me.
Wait, hold on there! This says otherwise: http://dlang.org/struct.html So what's up? Who's wrong!?
Jan 29 2013
next sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 05:24:56 UTC, Zach the Mystic 
wrote:
 On Wednesday, 30 January 2013 at 05:20:51 UTC, Zach the Mystic 
 wrote:
 You know, I was too dumb to even understand what you wrote 
 when I read it the first time. I was just naively assuming 
 that nested structs were like nested functions. Some rules 
 definitely need to be figured out here. I don't see why the 
 basic functionality which is provided for nested functions 
 couldn't work also for nested structs. Does "static struct" 
 mean anything here? Couldn't it be used exactly like static 
 nested functions? Would it break code if we now forced people 
 to say "static struct" instead of just struct?

 I'm sorry for missing your point. I'm trying to suggest 
 advanced language features without even knowing some of the 
 basics. I ask you to bear with me.
Wait, hold on there! This says otherwise: http://dlang.org/struct.html So what's up? Who's wrong!?
Okay, so now I'm really foolish! I think my ORIGINAL response, to use "outer", would actually make a lot of sense here! outer.outer.outer... ad infinitum. Not only, but when the struct has no data of its own, the compiler silently eliminates the pointer in question.
Jan 29 2013
parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 05:30:47 UTC, Zach the Mystic 
wrote:
 I'm sorry for missing your point. I'm trying to suggest 
 advanced language features without even knowing some of the 
 basics. I ask you to bear with me.
Wait, hold on there! This says otherwise: http://dlang.org/struct.html So what's up? Who's wrong!?
Okay, so now I'm really foolish! I think my ORIGINAL response, to use "outer", would actually make a lot of sense here! outer.outer.outer... ad infinitum. Not only, but when the struct has no data of its own, the compiler silently eliminates the pointer in question.
Or just your basic "no shadowing variables" rule to simplify, simplify... I can totally dig that.
Jan 29 2013
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 30, 2013 at 06:24:56AM +0100, Zach the Mystic wrote:
 On Wednesday, 30 January 2013 at 05:20:51 UTC, Zach the Mystic
 wrote:
You know, I was too dumb to even understand what you wrote when I
read it the first time. I was just naively assuming that nested
structs were like nested functions. Some rules definitely need to
be figured out here. I don't see why the basic functionality which
is provided for nested functions couldn't work also for nested
structs. Does "static struct" mean anything here? Couldn't it be
used exactly like static nested functions? Would it break code if
we now forced people to say "static struct" instead of just
struct?

I'm sorry for missing your point. I'm trying to suggest advanced
language features without even knowing some of the basics. I ask
you to bear with me.
Wait, hold on there! This says otherwise: http://dlang.org/struct.html So what's up? Who's wrong!?
That page only states the structs nested inside *functions* have a context pointer to the function's local variables. It says nothing about structs nested inside *structs*. (And yes, I looked. I was actually in the middle of writing something about using structs to simulate properties, and decided to look it up to be sure, and found that the spec actually doesn't say what I thought it said.) T -- Help a man when he is in trouble and he will remember you when he is in trouble again.
Jan 29 2013
parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 06:46:01 UTC, H. S. Teoh wrote:
 That page only states the structs nested inside *functions* 
 have a
 context pointer to the function's local variables. It says 
 nothing about
 structs nested inside *structs*. (And yes, I looked. I was 
 actually in
 the middle of writing something about using structs to simulate
 properties, and decided to look it up to be sure, and found 
 that the
 spec actually doesn't say what I thought it said.)


 T
Okay, cool. Two questions remain: 1) How hard to implement structs nested in structs to mimic ones nested in functions? 2) How much code breakage?
Jan 30 2013
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
30-Jan-2013 21:02, Zach the Mystic пишет:
 On Wednesday, 30 January 2013 at 06:46:01 UTC, H. S. Teoh wrote:
 That page only states the structs nested inside *functions* have a
 context pointer to the function's local variables. It says nothing about
 structs nested inside *structs*. (And yes, I looked. I was actually in
 the middle of writing something about using structs to simulate
 properties, and decided to look it up to be sure, and found that the
 spec actually doesn't say what I thought it said.)


 T
Okay, cool. Two questions remain: 1) How hard to implement structs nested in structs to mimic ones nested in functions?
IMO if property is to be implemented in the library it has to include the field itself. (And I suspect properties should enjoy compiler support). Then something like: struct A{ Property!(int, filter, getter) prop; private: void func() { ... prop.raw = xxx; //direct write prop = yyy; //goes through setter } } where .raw is the field itself and there must be a way to let only struct itself have access to it. I have one method to get this but I don't like it - put this in each module: mixin PropertyForModule!(my_module); introducing a Property template in this module, with private .raw accessible thusly only in this module. Getter is then just any function that maps T => T, with x => x by default so can be omitted. Filter is something new but in essence it works like the following setter: void setter(T)(ref T val, T newVal) { val = filter(newVal); //filter may through } It's a bit more restrictive though so feel free to destroy.
 2) How much code
 breakage?
A lot + subtly wasting memory. -- Dmitry Olshansky
Jan 30 2013
next sibling parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 17:35:25 UTC, Dmitry Olshansky 
wrote:
 2) How much code
 breakage?
A lot + subtly wasting memory.
I still have to read your other comments, but the issue of wasting memory has already been addressed by suggesting that the compiler detect that absence of any actual data in the struct and prohibit taking its address or referring to "this" except perhaps in the case of "alias someFunction this" to allow it to call that function transparently. It would not give the function a pointer or anything, the goal being to truly harness the power of struct's semantics without paying a price in performance of any kind.
Jan 30 2013
prev sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 17:35:25 UTC, Dmitry Olshansky 
wrote:
 Okay, cool. Two questions remain: 1) How hard to implement 
 structs
 nested in structs to mimic ones nested in functions?
IMO if property is to be implemented in the library it has to include the field itself. (And I suspect properties should enjoy compiler support). Then something like: struct A{ Property!(int, filter, getter) prop; private: void func() { ... prop.raw = xxx; //direct write prop = yyy; //goes through setter } } where .raw is the field itself and there must be a way to let only struct itself have access to it. I have one method to get this but I don't like it - put this in each module: mixin PropertyForModule!(my_module); introducing a Property template in this module, with private .raw accessible thusly only in this module. Getter is then just any function that maps T => T, with x => x by default so can be omitted. Filter is something new but in essence it works like the following setter: void setter(T)(ref T val, T newVal) { val = filter(newVal); //filter may through } It's a bit more restrictive though so feel free to destroy.
Does my suggestion about the compiler detecting a struct with no data warm you up a bit to properties as structs? If so, there is really very little need for ANY library support. Strangely, implementing struct-nested structs with actual data could be a significantly harder task than implementing them with no data. No extra pointer need be created. I'll try to summarize the changes required. The first two are necessary. The third is for aesthetic reasons, but as in the case of lambda functions that could make a huge difference. 1. Have non-static struct-nested structs be able to refer to their parent's data. With no-data structs, I suspect this will be easy, and it's actually the primary intended usage, which makes only the corner case tricky, where new pointers must be added, etc. 2. Add opGet to the compiler's list of overloads. It is simply opCall but with parens banned instead of mandated. 3. Enable Highlander structs so that defining a property is as easy as define a new-style lamba: struct __fooHidden {} __fooHidden foo; simply becomes: foo struct {}
Jan 30 2013
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
30-Jan-2013 22:05, Zach the Mystic пишет:
 On Wednesday, 30 January 2013 at 17:35:25 UTC, Dmitry Olshansky wrote:
 Okay, cool. Two questions remain: 1) How hard to implement structs
 nested in structs to mimic ones nested in functions?
IMO if property is to be implemented in the library it has to include the field itself. (And I suspect properties should enjoy compiler support). Then something like: struct A{ Property!(int, filter, getter) prop; private: void func() { ... prop.raw = xxx; //direct write prop = yyy; //goes through setter } } where .raw is the field itself and there must be a way to let only struct itself have access to it. I have one method to get this but I don't like it - put this in each module: mixin PropertyForModule!(my_module); introducing a Property template in this module, with private .raw accessible thusly only in this module. Getter is then just any function that maps T => T, with x => x by default so can be omitted. Filter is something new but in essence it works like the following setter: void setter(T)(ref T val, T newVal) { val = filter(newVal); //filter may through } It's a bit more restrictive though so feel free to destroy.
Does my suggestion about the compiler detecting a struct with no data warm you up a bit to properties as structs? If so, there is really very little need for ANY library support. Strangely, implementing struct-nested structs with actual data could be a significantly harder task than implementing them with no data. No extra pointer need be created.
I have one key problem - the hidden pointer detail. In other words how should it find the instance of the outer struct to to access it? struct A{ int a; struct B{ void foo(){ a = 42; } } B b; } A a; a.b.foo(); //how that b is supposed to know it's outer struct without the hidden pointer? auto x = a.b; x.foo();// and now what?
 I'll try to summarize the changes required. The first two are necessary.
 The third is for aesthetic reasons, but as in the case of lambda
 functions that could make a huge difference.

 1. Have non-static struct-nested structs be able to refer to their
 parent's data. With no-data structs, I suspect this will be easy, and
 it's actually the primary intended usage, which makes only the corner
 case tricky, where new pointers must be added, etc.

 2. Add opGet to the compiler's list of overloads. It is simply opCall
 but with parens banned instead of mandated.

 3. Enable Highlander structs so that defining a property is as easy as
 define a new-style lamba:
 struct __fooHidden {}
 __fooHidden foo;

 simply becomes:

 foo struct {}
-- Dmitry Olshansky
Jan 30 2013
next sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 18:36:17 UTC, Dmitry Olshansky 
wrote:
 I have one key problem - the hidden pointer detail.
 In other words how should it find the instance of the outer 
 struct to to access it?

 struct A{
 	int a;
 	struct B{
 		void foo(){ a = 42; }
 	}	
 	B b;
 }

 A a;
 a.b.foo(); //how that b is supposed to know it's outer struct 
 without the hidden pointer?

 auto x = a.b;
 x.foo();// and now what?
It seems struct B does need a pointer... and yet it only needs it in compile time. "auto x = a.b" is no different from "alias a.b x", because when you have no data, you have no data to worry about! a.b is nothing but a namespace with full struct semantics. I can only say that my intuition tells me that this is easily managed. I cannot tell you what adjustments need to be made to the compiler to get this to come out right. But what if B actually had some data? The only solution is to have one pointer for every struct it's nested inside of. I can imagine it getting tricky in this case. If it were so tricky as to be prohibitive to implement, then all is not lost. You can still implement zero-data structs as properties. In that case, I suggest weaving the implementation in directly with the Highlanders, because Highlanders will be much less appealing for any other use.
Jan 30 2013
next sibling parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 21:41:58 UTC, Zach the Mystic 
wrote:
 But what if B actually had some data? The only solution is to 
 have one pointer for every struct it's nested inside of. I can 
 imagine it getting tricky in this case. If it were so tricky as 
 to be prohibitive to implement, then all is not lost. You can 
 still implement zero-data structs as properties. In that case, 
 I suggest weaving the implementation in directly with the 
 Highlanders, because Highlanders will be much less appealing 
 for any other use.
I should correct myself, I think. You need one pointer for every struct nested which actually holds data. And I take back the connection between Highlanders and zero-data structs. The other possible use for Highlanders which I was thinking of is for quick prototyping, since "foo struct {}" is much easier to type than "struct Foo {}; Foo foo;". I see no real problem with these even if non-static, non-zero-data nested structs are not allowed access to their parent structs.
Jan 30 2013
prev sibling parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 21:41:58 UTC, Zach the Mystic 
wrote:
 On Wednesday, 30 January 2013 at 18:36:17 UTC, Dmitry Olshansky 
 wrote:
 I have one key problem - the hidden pointer detail.
 In other words how should it find the instance of the outer 
 struct to to access it?

 struct A{
 	int a;
 	struct B{
 		void foo(){ a = 42; }
 	}	
 	B b;
 }

 A a;
 a.b.foo(); //how that b is supposed to know it's outer struct 
 without the hidden pointer?

 auto x = a.b;
 x.foo();// and now what?
It seems struct B does need a pointer... and yet it only needs it in compile time. "auto x = a.b" is no different from "alias a.b x", because when you have no data, you have no data to worry about! a.b is nothing but a namespace with full struct semantics. I can only say that my intuition tells me that this is easily managed. I cannot tell you what adjustments need to be made to the compiler to get this to come out right. But what if B actually had some data? The only solution is to have one pointer for every struct it's nested inside of. I can imagine it getting tricky in this case. If it were so tricky as to be prohibitive to implement, then all is not lost. You can still implement zero-data structs as properties. In that case, I suggest weaving the implementation in directly with the Highlanders, because Highlanders will be much less appealing for any other use.
What I meant with regards to weaving the implementation with the Highlanders is that if empty structs have access to the outer nest then they are fundamentally different, and being so, will require a syntax to distinguish them. I don't readily have another easy syntax, so they would be forced to monopolize the Highlander syntax as punishment for being so inflexible.
Jan 30 2013
prev sibling next sibling parent "Rob T" <alanb ucora.com> writes:
On Wednesday, 30 January 2013 at 18:36:17 UTC, Dmitry Olshansky 
wrote:
[...]
 I have one key problem - the hidden pointer detail.
 In other words how should it find the instance of the outer 
 struct to to access it?

 struct A{
 	int a;
 	struct B{
 		void foo(){ a = 42; }
 	}	
 	B b;
 }

 A a;
 a.b.foo(); //how that b is supposed to know it's outer struct 
 without the hidden pointer?

 auto x = a.b;
 x.foo();// and now what?
Good point. A property-struct could behave like a struct and also like a regular member function. Member functions work because they take in a pointer to the struct or class when called, eg a.b.foo(); becomes a.b.foo(&a); auto x = a.b; // returns property value of b, not b itself. auto x = &a.b; // returns delegate pointer to b x.foo(); // OK --rt
Jan 30 2013
prev sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Wednesday, 30 January 2013 at 18:36:17 UTC, Dmitry Olshansky 
wrote:
 I have one key problem - the hidden pointer detail. In other 
 words how should it find the instance of the outer struct to to 
 access it?

 struct A{
 	int a;
 	struct B{
 		void foo(){ a = 42; }
 	}	
 	B b;
 }

 A a;
 a.b.foo(); //how that b is supposed to know it's outer struct 
 without the hidden pointer?

 auto x = a.b;
 x.foo();// and now what?
Been thinking about that. I can only think that inner (non-static) struts cannot be returned outside of their parent. Passing them to something is fine, but they in turn can't return them anywhere either. Actually they likely can't be set anywhere outside the ownership of the struct period; That would allow flexibility where we don't have it. //above struct example class C { A.B b; //Error, context pointer goes to type of A //need root struct. } A.B func(); //Error! ref A.B func(); //Error! A.B* func(); // system only, advanced programmers void func(A.B b); //acceptable void func(ref A.B b); //acceptable A func(A a); //allowed Now if one inner struct passed to another of the same struct (but not instance of), the context pointer can only point to the one that now owns it. For POD and other stuff this is acceptable. struct S { struct Inner{} Inner[] x; } S s1; S s2; s2.x = s1.x.dup; //allowed, same structure but //s2 owns it, pointers updated Although it seems limiting, it actually would be quite useful. I have a sorting algorithmn I'm testing with that would benefit from not being forced to use static. Seeing as the inner struct never leaves the outer struct... struct MCS { Range[] ranges; static struct Range{ MCS *parent; //manually managed. More error prone. } }
Jan 31 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
I kind of like this strange love child of a namespace and a 
variable that you're talking about. But I wouldn't call it a 
struct. I wouldn't make empty structs be these weird special 
cases which behave completely differently. Instead, I'd introduce 
a new keyword for this... thing.
Jan 30 2013
prev sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 18:05:08 UTC, Zach the Mystic 
wrote:
 [..]
How about using this new namespace_thingy keyword: struct S { private int value; namespace_thingy prop { int get() { return value; } prop opAssign(int v) { value = v; return prop; } // int data; // error: can't have data here } } The compiler would implicitly create something like: struct S { private int value; int prop_get() { return value; } int prop_opAssign(int v) { value = v; return prop_get(); } } ... S s; int v1 = s.prop; // lowered to s.prop_get() int v2 = (s.prop = v1); // lowered to s.prop_opAssign(v1) assert(v1 == v2);
Jan 30 2013
next sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 19:44:43 UTC, TommiT wrote:
 On Wednesday, 30 January 2013 at 18:05:08 UTC, Zach the Mystic 
 wrote:
 [..]
How about using this new namespace_thingy keyword: struct S { private int value; namespace_thingy prop { int get() { return value; } prop opAssign(int v) { value = v; return prop; } // int data; // error: can't have data here } } The compiler would implicitly create something like: struct S { private int value; int prop_get() { return value; } int prop_opAssign(int v) { value = v; return prop_get(); } } ... S s; int v1 = s.prop; // lowered to s.prop_get() int v2 = (s.prop = v1); // lowered to s.prop_opAssign(v1) assert(v1 == v2);
I think you're thinking along the right lines, but this is no better than what's already been suggested. From everything I've read, reusing old keywords to do new things in new places is a time-honored D tradition, and adding new ones is very much frowned upon. Also, because the "namespace_thingy"s have so much in common with structs, I think it would be misleading to call them something else. The lowering you're talking about already happens, in its own way, with the operator overloads. In fact, these overloads were designed *specifically* to allow struct and class instances to meld in seamlessly with built-in types. Just look at Walter's recent article for half floats. The whole article demonstrates D's ability to do exactly what everybody is trying to get their properties to do. Now we just have to get rid of the performance overhead by treating structs with no data as namespaces instead of the more commonly held perception that they are only supposed to work on their own data! Hurrah!
Jan 30 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 21:58:53 UTC, Zach the Mystic 
wrote:
 Also, because the "namespace_thingy"s have so much in common 
 with structs, I think it would be misleading to call them 
 something else.
The problem of using empty struct variables is that they take up memory. They have to, because you can make a pointer to a variable and then you can dereference that variable. There has to be at least a byte of memory to dereference. So, really, the only zero-overhead way to do this is to introduce a new keyword that creates something that you can't take the address of, because it kind of doesn't exist (like a namespace). It exists only in the sense that it can be used to tell the compiler which operators and functions to call. That's what my namespace_thingy is.
Jan 30 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01/30/2013 11:30 PM, TommiT wrote:
 On Wednesday, 30 January 2013 at 21:58:53 UTC, Zach the Mystic wrote:
 Also, because the "namespace_thingy"s have so much in common with
 structs, I think it would be misleading to call them something else.
The problem of using empty struct variables is that they take up memory. They have to, because you can make a pointer to a variable and then you can dereference that variable. There has to be at least a byte of memory to dereference.
No, that is not why. The only conceivable reason empty structs take up space is so that different instances have different addresses.
 So, really, the only zero-overhead way to do this is to introduce a new
 keyword that creates something that you can't take the address of,
 because it kind of doesn't exist (like a namespace). It exists only in
 the sense that it can be used to tell the compiler which operators and
 functions to call. That's what my namespace_thingy is.
Jan 30 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 22:41:23 UTC, Timon Gehr wrote:
 The problem of using empty struct variables is that they take 
 up memory.
 They have to, because you can make a pointer to a variable and 
 then you
 can dereference that variable. There has to be at least a byte 
 of memory
 to dereference.
No, that is not why. The only conceivable reason empty structs take up space is so that different instances have different addresses.
Oh, thanks for clearing that up.
Jan 30 2013
prev sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 22:30:10 UTC, TommiT wrote:
 On Wednesday, 30 January 2013 at 21:58:53 UTC, Zach the Mystic 
 wrote:
 Also, because the "namespace_thingy"s have so much in common 
 with structs, I think it would be misleading to call them 
 something else.
The problem of using empty struct variables is that they take up memory. They have to, because you can make a pointer to a variable and then you can dereference that variable. There has to be at least a byte of memory to dereference. So, really, the only zero-overhead way to do this is to introduce a new keyword that creates something that you can't take the address of, because it kind of doesn't exist (like a namespace). It exists only in the sense that it can be used to tell the compiler which operators and functions to call. That's what my namespace_thingy is.
I disagree. The compiler can easily tell if a struct is defined with no data, and simply optimize away the pointer in the process.
Jan 30 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 00:15:36 UTC, Zach the Mystic 
wrote:
 The problem of using empty struct variables is that they take 
 up memory. They have to, because you can make a pointer to a 
 variable and then you can dereference that variable. There has 
 to be at least a byte of memory to dereference.

 So, really, the only zero-overhead way to do this is to 
 introduce a new keyword that creates something that you can't 
 take the address of, because it kind of doesn't exist (like a 
 namespace). It exists only in the sense that it can be used to 
 tell the compiler which operators and functions to call. 
 That's what my namespace_thingy is.
I disagree. The compiler can easily tell if a struct is defined with no data, and simply optimize away the pointer in the process.
Here's Bjarne Stroustrup's reasoning for this design choice http://www.stroustrup.com/bs_faq2.html#sizeof-empty Q: Why is the size of an empty class not zero? A: To ensure that the addresses of two different objects will be different. For the same reason, "new" always returns pointers to distinct objects. Consider: class Empty { }; void f() { Empty a, b; if (&a == &b) cout << "impossible: report error to compiler supplier"; Empty* p1 = new Empty; Empty* p2 = new Empty; if (p1 == p2) cout << "impossible: report error to compiler supplier"; } There is an interesting rule that says that an empty base class need not be represented by a separate byte: struct X : Empty { int a; // ... }; void f(X* p) { void* p1 = p; void* p2 = &p->a; if (p1 == p2) cout << "nice: good optimizer"; } This optimization is safe and can be most useful. It allows a programmer to use empty classes to represent very simple concepts without overhead. Some current compilers provide this "empty base class optimization".
Jan 30 2013
parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Thursday, 31 January 2013 at 01:14:36 UTC, TommiT wrote:
 Here's Bjarne Stroustrup's reasoning for this design choice
 http://www.stroustrup.com/bs_faq2.html#sizeof-empty

 Q: Why is the size of an empty class not zero?

 A: To ensure that the addresses of two different objects will 
 be different. For the same reason, "new" always returns 
 pointers to distinct objects. Consider:

 class Empty { };

 void f()
 {
     Empty a, b;
     if (&a == &b) cout << "impossible: report error
                            to compiler supplier";

     Empty* p1 = new Empty;
     Empty* p2 = new Empty;
     if (p1 == p2) cout << "impossible: report error
                            to compiler supplier";
 }

 There is an interesting rule that says that an empty base class 
 need not be represented by a separate byte:

 struct X : Empty {
     int a;
     // ...
 };

 void f(X* p)
 {
     void* p1 = p;
     void* p2 = &p->a;
     if (p1 == p2) cout << "nice: good optimizer";
 }

 This optimization is safe and can be most useful. It allows a 
 programmer to use empty classes to represent very simple 
 concepts without overhead. Some current compilers provide this 
 "empty base class optimization".
Now classes are a different kettle of fish. I haven't thought them out and I don't think I need to. They may work seamlessly with my idea or be fraught with problems, I don't know. But there will never be a need for a new empty struct. The operator new makes no sense. There's no data! Imagine an empty struct as a list of functions under a namespace. That's all it really is. Except it just so happens that this namespace has a bunch of built-in functions which allow it to appear in normal code as if it's a *type*. Want it to appear with parens, so it looks like a function you're calling? Just define opCall inside the struct. Want it to appear before an equals sign. Just define opAssign. As an array? opIndex. A basic type such as an int? opGet. There will never be any need to create an instance. You only need one. At runtime, there is no evidence of a pointer at all, just normal data being passed to normal functions.
Jan 30 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 03:35:32 UTC, Zach the Mystic 
wrote:
 Now classes are a different kettle of fish.
Sorry for not being more explicit. Bjarne Stroustrup is the creator of C++, so the Q&A I posted refers to C++, not D. But, long story short, all of the Q&A is applicaple to D, as long as you read every instance of the word 'class' as 'struct'. Empty structs taking memory is just a fundamental language design thing, it's not something we can discuss, agree/disagree, or change.
Jan 31 2013
parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Thursday, 31 January 2013 at 12:44:18 UTC, TommiT wrote:
 On Thursday, 31 January 2013 at 03:35:32 UTC, Zach the Mystic 
 wrote:
 Now classes are a different kettle of fish.
Sorry for not being more explicit. Bjarne Stroustrup is the creator of C++, so the Q&A I posted refers to C++, not D. But, long story short, all of the Q&A is applicaple to D, as long as you read every instance of the word 'class' as 'struct'. Empty structs taking memory is just a fundamental language design thing, it's not something we can discuss, agree/disagree, or change.
I hope this isn't the final word on the topic.
Jan 31 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 13:58:44 UTC, Zach the Mystic 
wrote:
 I hope this isn't the final word on the topic.
I found this article which I think explains the reasoning behind non-zero sized structs even better: http://bytes.com/topic/c/insights/660463-sizeof-empty-class-structure-1-a
Jan 31 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 19:44:43 UTC, TommiT wrote:
 [..]
More of fleshing out of namespace_thingy: struct S { private: int m_startTime; int m_endTime; int m_duration; public: namespace_thingy start { int opGet() const { return m_startTime; } start opAssign(int t) { m_startTime = t; m_duration = m_endTime - m_startTime; return start; } } namespace_thingy end { int opGet() const { return m_endTime; } end opAssign(int t) { m_endTime = t; m_duration = m_endTime - m_startTime; return end; } dur opAssign(string s : "+")(int t) { m_endTime += t; m_duration = m_endTime - m_startTime; return dur; } } namespace_thingy dur { dur opAssign(int t) { m_duration = t; m_endTime = m_startTime + m_duration; return dur; } bool opEquals(int t) const { return m_duration == t; } } this(int t) { start = 0; end = t; assert(dur == 100); } } ... S s; s.start = 1; s.end = 8; assert(s.dur == 7); s.end += 3; assert(s.dur == 10); int v = s.start; s.start += 42; // error: no match for opAssign!"+"(S.start, int) // nor opAssign!"+"(int, int) int d = s.dur; // error: no conversion from S.dur to int
Jan 30 2013
prev sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
I think my former unholy union of namespace and variable is just 
too confusing to reason about, so here's my new way of seeing 
effectively the same thing, but with simpler semantics:

Keyword property is like the keyword struct, except:

1) properties can't have data members
2) properties can't have static member functions
3) properties can't have constructors/destructors
4) property instances always point to null
5) property instances' size is 0
6) if a property is declared inside a class/struct, all its
    member functions receive an implicit 'outer' variable
    which references the encapsulating object. Whether or
    not 'outer' is const ref or ref depends only on the
    constness of the encapsulating object.

Example:

struct T
{
     private int value;

     property PropType
     {
          property int getIt() const
         {
             return outer.value;
         }

         alias this = getIt;

         PropType opAssign(int v) const
         {
             outer.value = v;
             return this;
         }

         PropType opAssign(string s : "+")(int v) const
         {
             outer.value += v;
             return this;
         }

         bool isCorrect() const
         {
             return outer.value == 42;
         }
     }

     PropType myProp;
}

Usage:

T t;
int v = t.myProp; // lowers to t.myProp.getIt due to alias this
t.myProp = 41;
t.myProp += 1;
assert(t.myProp.isCorrect());
assert(&t.myProp == null);
assert(T.PropType.sizeof == 0);
Jan 30 2013
next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 02:10:51 UTC, TommiT wrote:
 Keyword property is like the keyword struct, except:

 1) properties can't have data members
 2) properties can't have static member functions
 3) properties can't have constructors/destructors
 4) property instances always point to null
 5) property instances' size is 0
 6) if a property is declared inside a class/struct, all its
    member functions receive an implicit 'outer' variable
    which references the encapsulating object. Whether or
    not 'outer' is const ref or ref depends only on the
    constness of the encapsulating object.
(CONT'D) Instances of such nested properties can be default- constructed only in the lexical scope where the property type is defined.
Jan 30 2013
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01/31/2013 03:10 AM, TommiT wrote:
 I think my former unholy union of namespace and variable is just too
 confusing to reason about, so here's my new way of seeing effectively
 the same thing, but with simpler semantics:

 Keyword property is like the keyword struct, except:

 1) properties can't have data members
 2) properties can't have static member functions
 3) properties can't have constructors/destructors
 4) property instances always point to null
 5) property instances' size is 0
 6) if a property is declared inside a class/struct, all its
     member functions receive an implicit 'outer' variable
     which references the encapsulating object. Whether or
     not 'outer' is const ref or ref depends only on the
     constness of the encapsulating object.

 Example:
...
 Usage:

 T t;
 int v = t.myProp; // lowers to t.myProp.getIt due to alias this
 t.myProp = 41;
 t.myProp += 1;
 assert(t.myProp.isCorrect());
 assert(&t.myProp == null);
 assert(T.PropType.sizeof == 0);
auto v = t.myProp; // ?
Jan 31 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 09:39:06 UTC, Timon Gehr wrote:
 auto v = t.myProp; // ?
I think it might be fine to allow copy-constructing 'properties', passing them to functions by value or by reference, and such. So, you could do: void func(ref T.PropType v); PropType v = t.myProp; func(v); But default-construction of 'properties' can be allowed only nested inside the enclosing object (to ensure that the enclosing object exists, and can be passed implicitly as that 'outer' variable to PropType's methods). Although, I don't see much real value in allowing the kind of code above. So, it might be just as well to just disallow copying of 'properties' except when it happens as a part of copying the enclosing object.
Jan 31 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 12:58:13 UTC, TommiT wrote:
 [..] So, it might be just as well to just disallow copying of 
 'properties' except when it happens as a part of copying the 
 enclosing object.
Or... maybe not, because disallowing making a copy of a property variable makes it illegal to pass it by value as a templated argument: struct S { int n; property Prop { property int get() { return outer.n; } alias this = get; } Prop prop; } void foo(int v) {} void bar(T)(T t) if (isImplicitlyConvertible!(T,int)) { int v = t; } ... S s; foo(s.prop); // ok: calling foo(s.prop.get) bar(s.prop); // error: cannot make a copy of a // property variable of type S.Prop
Jan 31 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 14:47:17 UTC, TommiT wrote:
 [..]
Although, it's just not very healthy to be passing those property variables around, because it enables writing all kinds of bugs: struct S { int n; property Prop { property int get() { return outer.n; } alias this = get; Prop opAssign(string S : "+")(int v) { outer.n += v; return this; } } Prop prop; } void foo(T)(T t) if (isImplicitlyConvertible!(T,int)) { auto v = t; v += 1; // calls t.prop.opAssign!"+"(1) // given: is(T == S.Prop) } ... S s; foo(s.prop); // increments S.n, not good So, I guess properties need to be magic, they're not imaginable as a some kind of restricted + augmented structs. The crux of the matter is the alias this which is needed for implementing the getter (accessor) of the property. The property variable should *not* be convertible to getter, but the property variable itself should be the getter.
Jan 31 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 15:18:36 UTC, TommiT wrote:
 [..] So, I guess properties need to be magic, [..]
So, going back to the concept of property as a crossbreed between a namespace and a variable. Here's my new take on it: * Property looks like a namespace when it is defined, and a variable when it is used. * Property definition creates its own lexical scope. * Properties can't hold any variables, don't take up any memory, and you can't take the address of a property. * Properties may have a special opGet operator that will be called eagerly whenever a naked property is used, i.e. a property is used without applying any such operator or member function that is defined for that particular property. I don't know if properties really need to have member functions, but I wouldn't want to rule them out right off the bat. struct T { private int value; bool isCorrect() const { return false; } property myProp { // the special "eager casting operator" of properties int opGet() const { return value; } void opAssign(int v) { value = v; // doesn't have to return anything } void opAssign(string s : "+")(int v) { // 'this' refers to the enclosing object this.value += v; } // function attribute 'const' refers to the // constness of the enclosing object bool isCorrect() const { return value == 42; } ref T sayWhat() { // isCorrect refers to: this.myProp.isCorrect // ...and not to: this.isCorrect assert(isCorrect()); // 'this' refers to the enclosing object return this; } int data; // error: properties can't hold any variables } } void foo(T : int)(T t); int fn(T.myProp p); // error: T.myProp is a property, not a type ... T t; foo(t.myProp); // foo(t.myProp.opGet()); auto v = t.myProp; // int v = t.myProp.opGet(); t.myProp = 41; // t.myProp.opAssign(41); t.myProp += 1; // t.myProp.opAssign!"+"(41); assert(t.myProp.isCorrect()); immutable T t2; t2.myProp.sayWhat(); // error: cannot call a non-const property // method of an immmutable variable t2
Jan 31 2013
next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 16:44:03 UTC, TommiT wrote:
 T t;
 foo(t.myProp);     // foo(t.myProp.opGet());
 auto v = t.myProp; // int v = t.myProp.opGet();
 t.myProp = 41;     // t.myProp.opAssign(41);
 t.myProp += 1;     // t.myProp.opAssign!"+"(41);
 assert(t.myProp.isCorrect());

 immutable T t2;
 t2.myProp.sayWhat(); // error: cannot call a non-const property
                      // method of an immmutable variable t2
(CONT'D) t.myProp *= 2; // error: can't assign to an rvalue int It tries to call: t.myProp.opGet() *= 2; ...because T.myProp doesn't define a *= operator. So, it becomes one of those "naked" uses of a property.
Jan 31 2013
prev sibling next sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 16:44:03 UTC, TommiT wrote:
 So, going back to the concept of property as a crossbreed 
 between a namespace and a variable. Here's my new take on it:

 * Property looks like a namespace when it is defined, and a 
 variable when it is used.

 * Property definition creates its own lexical scope.

 * Properties can't hold any variables, don't take up any 
 memory, and you can't take the address of a property.

 * Properties may have a special opGet operator that will be 
 called eagerly whenever a naked property is used, i.e. a 
 property is used without applying any such operator or member 
 function that is defined for that particular property.

 I don't know if properties really need to have member 
 functions, but I wouldn't want to rule them out right off the 
 bat.

 struct T
 {
     private int value;

     bool isCorrect() const
     {
         return false;
     }

     property myProp
     {
         // the special "eager casting operator" of properties
         int opGet() const
         {
             return value;
         }

         void opAssign(int v)
         {
             value = v;
             // doesn't have to return anything
         }

         void opAssign(string s : "+")(int v)
         {
             // 'this' refers to the enclosing object
             this.value += v;
         }

         // function attribute 'const' refers to the
         // constness of the enclosing object
         bool isCorrect() const
         {
             return value == 42;
         }

         ref T sayWhat()
         {
             // isCorrect refers to: this.myProp.isCorrect
             // ...and not to:       this.isCorrect
             assert(isCorrect());

             // 'this' refers to the enclosing object
             return this;
         }

         int data; // error: properties can't hold any variables
     }
 }

 void foo(T : int)(T t);

 int fn(T.myProp p); // error: T.myProp is a property, not a type

 ...

 T t;
 foo(t.myProp);     // foo(t.myProp.opGet());
 auto v = t.myProp; // int v = t.myProp.opGet();
 t.myProp = 41;     // t.myProp.opAssign(41);
 t.myProp += 1;     // t.myProp.opAssign!"+"(41);
 assert(t.myProp.isCorrect());

 immutable T t2;
 t2.myProp.sayWhat(); // error: cannot call a non-const property
                      // method of an immmutable variable t2
Here's some more elaboration of the interplay between 1) "naked" property use, 2) property methods/operators and 3) the methods/operators of the type which "naked" property use returns through the opGet operator: struct Speed { int _value; void opOpAssign(string op)(int rhs) if (op == "+") { _value += rhs; assert(1 <= _value && _value <= 4); } } struct Sneak { int _value; void opOpAssign(string op)(int rhs) if (op == "+") { _value += rhs; assert(1 <= _value && _value <= 4); } } struct Character { private Speed _speed; private Sneak _sneak; property speed { ref Speed opGet() { return _speed; } void opOpAssign(string op)(int rhs) if (op == "+") { _speed += rhs; assert(_speed._value + _sneak._value <= 5); } } property sneak { ref Sneak opGet() { return _sneak; } } } void main() { Character c; c.speed += 1; // calls: c.speed.opOpAssign!"+"(1); c.sneak += 1; // calls: c.sneak.opGet() += 1; // i.e. c._sneak.opOpAssign!"+"(1); } As far as I know, the obove kind of thing isn't possible with the current state of affairs; with property attribute, you cannot provide the extra level of encapsulation that checks that the sum of 'speed' and 'sneak' isn't above 5.
Feb 01 2013
parent reply "TommiT" <tommitissari hotmail.com> writes:
On Friday, 1 February 2013 at 14:49:48 UTC, TommiT wrote:
 As far as I know, the obove kind of thing isn't possible with 
 the current state of affairs; with  property attribute, you 
 cannot provide the extra level of encapsulation that checks 
 that the sum of 'speed' and 'sneak' isn't above 5.
...and if the specification is changed so that: struct S { T _prop; property T prop() const { return _prop; } property void prop(T t) { assert(t <= 5); _prop = t; } } ... S s; s.prop += 3; ...gets lowered to s.prop = (s.prop + 3); Then that sucks because now T is forced to provide the binary + operator.
Feb 01 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, February 01, 2013 16:03:41 TommiT wrote:
 On Friday, 1 February 2013 at 14:49:48 UTC, TommiT wrote:
 As far as I know, the obove kind of thing isn't possible with
 the current state of affairs; with  property attribute, you
 cannot provide the extra level of encapsulation that checks
 that the sum of 'speed' and 'sneak' isn't above 5.
...and if the specification is changed so that: struct S { T _prop; property T prop() const { return _prop; } property void prop(T t) { assert(t <= 5); _prop = t; } } ... S s; s.prop += 3; ...gets lowered to s.prop = (s.prop + 3); Then that sucks because now T is forced to provide the binary + operator.
And why is that a problem? If you expect s.pop += 3 to work, how could you not expect + to work on the type of prop? You're generally doing something wrong if your type defines += and not + anyway, so I really don't see the problem here. - Jonathan M Davis
Feb 01 2013
parent "TommiT" <tommitissari hotmail.com> writes:
On Friday, 1 February 2013 at 15:14:55 UTC, Jonathan M Davis 
wrote:
 [..] You're generally doing something wrong
 if your type defines += and not + anyway, so I really don't see 
 the problem
 here.
Okay, I suppose you're right. But that still leaves the possible performance overhead with s.prop = (s.prop + 3). For example, if prop is an array of values, there's no point in making a copy of it, when you could more effectively just add 3 to each element of the array.
Feb 01 2013
prev sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Thursday, 31 January 2013 at 16:44:03 UTC, TommiT wrote:
 So, going back to the concept of property as a crossbreed 
 between a namespace and a variable. Here's my new take on it:
 [..]
Let's scratch that idea. See this instead: http://forum.dlang.org/thread/poiplzvdkfrdohobniif forum.dlang.org
Feb 02 2013
prev sibling parent reply "TommiT" <tommitissari hotmail.com> writes:
On Wednesday, 30 January 2013 at 17:02:44 UTC, Zach the Mystic 
wrote:
 [..] 1) How hard to implement structs nested in structs to 
 mimic ones nested in functions?
Given: struct Outer { struct Inner { int n1; } int n2; } Outer.sizeof should be equal to 2 * int.sizeof, because there's no point in introducing any overhead here. Whereas structs inside functions do their magic by having an implicit pointer, which increases their size. I don't think we want to introduce any memory overhead with something as insignificant as properties.
Jan 30 2013
parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 17:42:48 UTC, TommiT wrote:
 On Wednesday, 30 January 2013 at 17:02:44 UTC, Zach the Mystic 
 wrote:
 [..] 1) How hard to implement structs nested in structs to 
 mimic ones nested in functions?
Given: struct Outer { struct Inner { int n1; } int n2; } Outer.sizeof should be equal to 2 * int.sizeof, because there's no point in introducing any overhead here. Whereas structs inside functions do their magic by having an implicit pointer, which increases their size. I don't think we want to introduce any memory overhead with something as insignificant as properties.
See my first answer to Dmitri's post. Most properties will hold no data of their own, and the compiler could detect this, thus eliminating the need for the pointer at runtime.
Jan 30 2013
prev sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Tuesday, 29 January 2013 at 18:54:09 UTC, H. S. Teoh wrote:
 	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;
 	}
Also, I has assumed that opCall or opGet would make more sense than how you did it: struct AreaProp { float opGet() { ... } ... } Why "alias this" in your version instead of opCall or opGet?
Jan 29 2013
parent reply "Rob T" <alanb ucora.com> writes:
On Wednesday, 30 January 2013 at 05:44:36 UTC, Zach the Mystic 
wrote:
 Why "alias this" in your version instead of opCall or opGet?
opCall requires parens, probably to disambiguate from alias this. string A { int i; int opCall() { return _i; } } A a, int i = a; // error int i = a(); // works You can however do this alias opCall this; int i = a; // works int i = a(); // works too --rt
Jan 29 2013
parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Wednesday, 30 January 2013 at 06:38:32 UTC, Rob T wrote:
 opCall requires parens, probably to disambiguate from alias 
 this.

 string A
 {
   int i;
   int opCall() { return _i; }
 }

 A a,

 int i = a; // error
 int i = a(); // works

 You can however do this

 alias opCall this;

 int i = a; // works
 int i = a(); // works too

 --rt
I guess that's how to do it. So now we have all three. Mandate parens with opCall. Allow both with alias opCall this, and disallow parens with opGet. So let's go back to my previous argument. Aside from a few language changes, such as making current nested structs into nested static structs, and introducing opGet, a property is nothing but a poor man's struct. Destroy.
Jan 30 2013
prev sibling parent reply "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Tuesday, 29 January 2013 at 17:47:44 UTC, Rob T wrote:
 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.
There is even more then one way to do it, and your new struct need not carry any data of its own: struct Steve { int _n; bool nHasBeenSet; n struct { int opGet() { return _n; } int opAssign( int newN ) { _n = newN; nHasBeenSet = true; return _n; } } } The extra data is outside the struct's property definition. I assume this would be the normal way to do it. Structs have incredible semantics, and they're already in the language. In my opinion, their use as a namespace is under-appreciated.
 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
Thank you for saying that, Rob T.
Jan 29 2013
parent reply "Rob T" <alanb ucora.com> writes:
On Tuesday, 29 January 2013 at 20:14:29 UTC, Zach the Mystic 
wrote:
 On Tuesday, 29 January 2013 at 17:47:44 UTC, Rob T wrote:
 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.
There is even more then one way to do it, and your new struct need not carry any data of its own: struct Steve { int _n; bool nHasBeenSet; n struct { int opGet() { return _n; } int opAssign( int newN ) { _n = newN; nHasBeenSet = true; return _n; } } } The extra data is outside the struct's property definition. I assume this would be the normal way to do it. Structs have incredible semantics, and they're already in the language. In my opinion, their use as a namespace is under-appreciated.
 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
Thank you for saying that, Rob T.
We can extend the idea to not only think in terms of "smart data", but also in terms of "smart processing", where a normal function is wrapped up into a struc like object so that it may optionally carry it's own data for holding state, and also optionally include additional functions for performing various additional operations. We can do this already using structs, and maybe only a few small tweaks and it's really nice to use. Same concept as the property, just that we're thinking in terms of the processing side of things instead of only the data storage. With what we're thinking about here, we can do both in one identical package. Maybe the whole debate concerning property points out that the stuct, and I suppose the class, are missing something, but it's not that we need to add properties to them, instead that we need to allow them to behave like properties, and perhaps more. Don't forget about modules in D, the module is a struct like concept, although you cannot have multiple instances of a module, it does contains state, has a constructor and destructor, has private and public members, and can also have properties using current property syntax. It is sort of nested, due to imports of other modules (imports the interfaces). It could use improvements to the interface, but it looks like a struct in many ways. Maybe take a look at the struct-like areas in D and think about what is needed to make them more versatile, rather than slap on extra gadgets to make up for the weaknesses. --rt
Jan 29 2013
parent "Zach the Mystic" <reachBUTMINUSTHISzach gOOGLYmail.com> writes:
On Tuesday, 29 January 2013 at 21:23:19 UTC, Rob T wrote:
 We can extend the idea to not only think in terms of "smart 
 data", but also in terms of "smart processing", where a normal 
 function is wrapped up into a struc like object so that it may 
 optionally carry it's own data for holding state, and also 
 optionally include additional functions for performing various 
 additional operations. We can do this already using structs, 
 and maybe only a few small tweaks and it's really nice to use.

 Same concept as the property, just that we're thinking in terms 
 of the processing side of things instead of only the data 
 storage. With what we're thinking about here, we can do both in 
 one identical package.

 Maybe the whole debate concerning  property points out that the 
 stuct, and I suppose the class, are missing something, but it's 
 not that we need to add properties to them, instead that we 
 need to allow them to behave like properties, and perhaps more.
Besides opGet, which would behave *exactly* like opCall except it disallows parentheses, what is really missing from structs? If you simply look at http://dlang.org/operatoroverloading.html , somebody, I don't know who, has completely designed everything you could possibly want a property to do. H.S. Teoh mentioned the "address of" operator "&". So simple... add opAddress, if necessary, to the list! A property is nothing more than a poor man's struct, from what I can see. I really think Walter should just take it out behind the woodshed and do what he has to do.
 Don't forget about modules in D, the module is a struct like 
 concept, although you cannot have multiple instances of a 
 module, it does contains state, has a constructor and 
 destructor, has private and public members, and can also have 
 properties using current  property syntax. It is sort of 
 nested, due to imports of other modules (imports the 
 interfaces). It could use improvements to the interface, but it 
 looks like a struct in many ways.
You know, anything, a module, a function, a struct, can contain state by declaring a static variable, i.e. "static int foo;" which I think is a per thread state. I'm not sure state is as bad a problem as you suggest. What would be the difference between the state you're talking about and this? Yes, modules have things in common with structs, but to go the whole way, are you suggesting that you want one *module* per *property*? No, I don't assume so. Structs could be thought of as mini-modules, but at least you can have more than one per file!
 Maybe take a look at the struct-like areas in D and think about 
 what is needed to make them more versatile, rather than slap on 
 extra gadgets to make up for the weaknesses.
To define all of the operators for an int property could be quite tedious, but a few carefully selected templates in a library somewhere, std.property maybe, could probably allieviated most if not all of the grunt work of implementing the full suite of operators needed to fully simulate a built-in type.
Jan 29 2013
prev sibling next sibling parent "Michael" <pr m1xa.com> writes:
Just cross posting proposal 
http://forum.dlang.org/post/rnwpxkmyihpzqlevhcoz forum.dlang.org

Thanks)
Feb 02 2013
prev sibling parent "eles" <eles eles.com> writes:
On Friday, 25 January 2013 at 18:57:17 UTC, Johannes Pfau wrote:
 As the property thread is growing big again, arguments get 
 lost, etc I
Maybe it's time for a new thread: "Wrap-up of the property discussion wrap-up"...
Feb 03 2013