February 08, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | First of all, thanks for this very insight full discussion. I start to understand the other side. :-) On Fri, 2013-02-08 at 15:13 -0500, Steven Schveighoffer wrote: > On Fri, 08 Feb 2013 14:05:40 -0500, Robert <jfanatiker@gmx.at> wrote: > > > On Fri, 2013-02-08 at 12:52 -0500, Steven Schveighoffer wrote: > >> > >> Then it doesn't conform to the range API, where front is a property. > > I can't find anything about that front has to be a property here: http://dlang.org/phobos/std_range.html#isInputRange > > > > all it states that you can get the current element by issuing: > > r.front > > which would still be possible with the optional parentheses of DIP23. > > Technically this is true. But the expectation is that r.front is a property/field, not a function. That aspect is difficult to capture in a template, especially in the current implementation where @property on a getter is essentially redundant info. In a template you should not care what it actually is, as long as this works: auto h=r.front; (stolen from the implementation of isForwardRange), if I understood your argument correctly. > > In fact, the only case where a properly-implemented @property would be required on a getter is for delegate properties. I would argue that if @property worked correctly, it would be possible and correct to require r.front to be a property in that template. I don't understand, what is the exact problem with: r.front()()? Despite not matching the specification regarding no parentheses for properties? > > Module level properties pose an issue, because @property does not designate whether a property is a getter or a setter. In particular, a single-arg property function could be considered both a module property setter, or a UFCS property getter. > > So your issue is not really that UFCS properties are wrong, it's that disabling module-level properties is wrong. I agree with you, but at the same time, module level properties are not as common or useful as UFCS. Sure UFCS for functions, I suggest that UFCS for properties does not make a lot of sense. (If you apply my definition of a property and accept simple functions with optional parentheses as better way.) I am sorry, I don't understand what you mean here with: > In fact, one could argue they are more confusing since module scope is one of the only scopes that can be obscured. > > There are other possible solutions, such as designating something as a getter specifically, or adding more syntax (i.e. decorating the first parameter as 'this' for a module-level UFCS getter). But for now, the easiest thing is to disallow one or the other, and since UFCS properties are pretty much essential in Phobos, he's disabling the module level properties. > > The 42.fun = 43 example, just about everything is wrong with that. > > Look here is another function that D "allows": > > void increment(int x); > > What is that exactly supposed to do? > > 42.increment(); // what? > increment(42); // what? > > Design and naming of functions is never going to be a problem that the compiler can solve. Point taken. But my point is, that with optional parentheses UFCS properties are not needed, so the cleaner solution is to forbid UFCS properties. (See my DIP for further reasoning when it is ready) > > My point on that was, if something works but is not supposed to, we shouldn't have to worry about keeping that code working. @property in its current form is NOT implemented as it was designed. There is no requirement to keep existing behavior that doesn't correctly work. > > In the example given, @property is supposed to ban the use of parentheses according to the spec, but it doesn't. In fact, in the case of a @property that returns a delegate, it REQUIRES the extra parentheses. This is a bug, and not something to try and keep working. Despite being inconsistent with the specification, what are the problems with that? > > On the other hand, ref @properties is NOT a bug, it functions as designed. Whether to remove it or not is certainly a discussion we can have, but it's not buggy behavior. See the difference? But the documentation of properties: http://dlang.org/property.html in the section about user defined properties also states in the very first sentence: Properties are functions that can be syntactically treated as if they were fields or variables. which is clearly not true at the moment. If this sentence got removed and replaced with a big fat warning that properties are not exchangeable with fields, this would be the least that should be done. The current thinking instead seems to be that properties are just syntactic sugar and the level of encapsulation is business that is left to the developer. My thinking on the other hand is, that properties are the method of encapsulation, so they should ensure it. If something does not, it is not a property, which leads to a very clean design of properties. |
February 08, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to FG | On Fri, 2013-02-08 at 22:15 +0100, FG wrote:
> Or should void front(T val) remain a
> @property, while ref T front() becomes a plain function?
Exactly.
|
February 09, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Friday, 8 February 2013 at 21:02:10 UTC, Andrei Alexandrescu wrote:
> On 2/8/13 2:05 PM, Robert wrote:
>> Look at the section "No module-level properties". Why not?! That's a
>> perfectly valid use of properties. The proposal disallows module-level
>> properties, but instead allows:
>> 42.fun = 43;
>> which reads like: assign 43 to the fun property of 42. We get this
>> really obscure feature but disallowing module-level properties? If that
>> is not wrong, than I don't know what is.
>
> There would be ambiguities with module level properties. A property with one argument may be either a setter for a module-level property or a getter for the property of a module-level object.
>
I think this was settled, allowing such property to only be a getter via UFCS.
|
February 10, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert | On Fri, 08 Feb 2013 16:45:58 -0500, Robert <jfanatiker@gmx.at> wrote: > First of all, thanks for this very insight full discussion. I start to > understand the other side. :-) You are welcome! I also am learning things and shaping my opinions based on these discussions. > On Fri, 2013-02-08 at 15:13 -0500, Steven Schveighoffer wrote: >> On Fri, 08 Feb 2013 14:05:40 -0500, Robert <jfanatiker@gmx.at> wrote: >> >> > all it states that you can get the current element by issuing: >> > r.front >> > which would still be possible with the optional parentheses of DIP23. >> >> Technically this is true. But the expectation is that r.front is a >> property/field, not a function. That aspect is difficult to capture in a >> template, especially in the current implementation where @property on a >> getter is essentially redundant info. > In a template you should not care what it actually is, as long as this > works: > auto h=r.front; > (stolen from the implementation of isForwardRange), if I understood your > argument correctly. In the grand scheme of things, the fact that a range's front accessor is a property or a function is not technically important. But front being a property IS important for specific uses. You can't have it so that front is a function only when using it as a range, and a property when using it as a delegate getter. >> In fact, the only case where a properly-implemented @property would be >> required on a getter is for delegate properties. I would argue that if >> @property worked correctly, it would be possible and correct to require >> r.front to be a property in that template. > I don't understand, what is the exact problem with: r.front()()? Despite > not matching the specification regarding no parentheses for properties? The problem is, isInputRange does not require that it is a function, but you ARE requiring that by specifying arbitrary rules. Imagine an infinite range (one which never ends), that returns exactly a specific delegate. Such a range could be implemented: struct infiniteDelegate { int delegate() front; enum empty = false; void popFront() {}; } Such a range would fit the definition of input range, but unlike an array, r.front() is how you call the delegate, not r.front()(). Making a consistent interface is not possible without @property. >> Module level properties pose an issue, because @property does not >> designate whether a property is a getter or a setter. In particular, a >> single-arg property function could be considered both a module property >> setter, or a UFCS property getter. >> >> So your issue is not really that UFCS properties are wrong, it's that >> disabling module-level properties is wrong. I agree with you, but at the >> same time, module level properties are not as common or useful as UFCS. > Sure UFCS for functions, I suggest that UFCS for properties does not > make a lot of sense. (If you apply my definition of a property and > accept simple functions with optional parentheses as better way.) Actually, that is not true. Check out the definition of hasAssignableElements: http://dlang.org/phobos/std_range.html#.hasAssignableElements By your requirements, hasAssignableElements!(T[]) would necessarily always be false, since UFCS properties are banned, so array.front is not a property. You are saying, I can't assign to the front element of an array. That doesn't sit well with the rest of array's API. > I am sorry, I don't understand what you mean here with: >> In fact, one could argue they are more confusing since module scope is one >> of the only scopes that can be obscured. int x; void foo() { int x; // allowed x = 5; // applies to local scope, not module { int x; // error! shadows outer scope x. } } So the point is, a global property could possibly be obscured if you declared a local variable with the same name. Note also that you can have struct-qualified properties (at least you should be able to, it seems Andrei's DIP does not allow it, but it's not at odds with UFCS). Those are just about equivalent to module-level properties, they just require specifying the struct name (and people have used this in clever ways to achieve good readable results). > >> >> There are other possible solutions, such as designating something as a >> getter specifically, or adding more syntax (i.e. decorating the first >> parameter as 'this' for a module-level UFCS getter). But for now, the >> easiest thing is to disallow one or the other, and since UFCS properties >> are pretty much essential in Phobos, he's disabling the module level >> properties. >> >> The 42.fun = 43 example, just about everything is wrong with that. >> >> Look here is another function that D "allows": >> >> void increment(int x); >> >> What is that exactly supposed to do? >> >> 42.increment(); // what? >> increment(42); // what? >> >> Design and naming of functions is never going to be a problem that the >> compiler can solve. > > Point taken. But my point is, that with optional parentheses UFCS > properties are not needed, so the cleaner solution is to forbid UFCS > properties. (See my DIP for further reasoning when it is ready) What about setters? >> >> My point on that was, if something works but is not supposed to, we >> shouldn't have to worry about keeping that code working. @property in its >> current form is NOT implemented as it was designed. There is no >> requirement to keep existing behavior that doesn't correctly work. >> >> In the example given, @property is supposed to ban the use of parentheses >> according to the spec, but it doesn't. In fact, in the case of a >> @property that returns a delegate, it REQUIRES the extra parentheses. >> This is a bug, and not something to try and keep working. > Despite being inconsistent with the specification, what are the problems > with that? I'm not saying it's a problem, I suppose it could be considered by some, desirable to require the extra parentheses. I haven't heard from that person yet. But "being backwards compatible" is not a good reason when you are talking about incorrect behavior. It would have to be because that is a desirable feature. Frankly, if that was the case, I think we would never have introduced @property. >> >> On the other hand, ref @properties is NOT a bug, it functions as >> designed. Whether to remove it or not is certainly a discussion we can >> have, but it's not buggy behavior. See the difference? > > But the documentation of properties: http://dlang.org/property.html > in the section about user defined properties also states in the very > first sentence: > Properties are functions that can be syntactically treated as > if they were fields or variables. > > which is clearly not true at the moment. If this sentence got removed > and replaced with a big fat warning that properties are not exchangeable > with fields, this would be the least that should be done. It certainly is true. They can be treated like fields for getting and setting, and I think "syntactically" means you access them like you would access a field. You might say "yeah, but you can't do ++ on them!", but you can't do that to a const field either. Should const fields be considered fields? A ref property pretty much behaves EXACTLY like a field. > The current thinking instead seems to be that properties are just > syntactic sugar and the level of encapsulation is business that is left > to the developer. I don't think that SHOULD be D's business. Encapsulation should be possible, encouraged, but not dictated. > My thinking on the other hand is, that properties are the method of > encapsulation, so they should ensure it. If something does not, it is > not a property, which leads to a very clean design of properties. Such a design is entirely possible today, even if ref properties exist, even if *properties* didn't exist. Why must you use ref properties just because they are available? Note that the reader/user of code is never guaranteed that they are working with properly encapsulated code. A property looks like a field, and a field is NOT encapsulated. So any compiler/language guarantees of encapsulation with properties are moot. -Steve |
February 10, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Ok, at the very first I have to make clear that my DIP is about changing the actual specification, not trying to make the implementation to match the specification, because in my view of things, the specification is the problem. Properties in my proposal are no longer about optional parentheses ore forbidden parentheses. Properties are a concept hat benefits from the fact, that parentheses are optional, but would work either way. Reducing properties to functions with no parentheses is just not getting it right as we can see with the problems we have. > > In the grand scheme of things, the fact that a range's front accessor is a property or a function is not technically important. But front being a property IS important for specific uses. You can't have it so that front is a function only when using it as a range, and a property when using it as a delegate getter. In my DIP a property is a function and they behave the same, except that prop=5; calls prop(5); if prop is a property. > > >> In fact, the only case where a properly-implemented @property would be required on a getter is for delegate properties. I would argue that if @property worked correctly, it would be possible and correct to require r.front to be a property in that template. > > I don't understand, what is the exact problem with: r.front()()? Despite > > not matching the specification regarding no parentheses for properties? > > The problem is, isInputRange does not require that it is a function, but you ARE requiring that by specifying arbitrary rules. I think this has to be fixed, because it solves all ambiguities and encourages good design. > > Imagine an infinite range (one which never ends), that returns exactly a specific delegate. Such a range could be implemented: > > struct infiniteDelegate > { > int delegate() front; > enum empty = false; > void popFront() {}; > } It would, based on the current definition, yes. But what is it good for? Why not simply write: struct infiniteDelegate { int delegate() front(); bool empty() @property { return false; } void popFront() {}; } Where the body of front for example can ensure that the delegate is actually set and not null or can change it if necessary, ... I haven't applied @property to front in this example, because it would not change anything, it would be required though if you provided a setter and wanted to have front=something; work. Whether or not you apply @property to read-only functions, depends on your use case, if you want proper encapsulation, you would add @property and in case you want a writable front at some time, you would add a setter. If you don't consider encapsulation necessary, you would probably do not add @property and return a ref if it has to be writable sometime. > Actually, that is not true. Check out the definition of hasAssignableElements: > > http://dlang.org/phobos/std_range.html#.hasAssignableElements > > By your requirements, hasAssignableElements!(T[]) would necessarily always be false, since UFCS properties are banned, so array.front is not a property. You are saying, I can't assign to the front element of an array. That doesn't sit well with the rest of array's API. Not true, the current definition of front for arrays is: @property ref T front(T)(T[] a); which would still work just the same, if you removed @property. Strings are interesting as front returns a non ref dchar (obviously), but they have no writable front anyway. If you would really have a use case where you need an actual setter/getter for an array for example, just do proper encapsulation in a struct/class. If you don't do that, your setter could be bypassed anyway so the design is questionable at best. (Which you could still achieve via alias this, if you really wanted that.) > void foo() > { > > int x; // allowed > > x = 5; // applies to local scope, not module > { > int x; // error! shadows outer scope x. > } > } > > So the point is, a global property could possibly be obscured if you declared a local variable with the same name. Same for instance variables and member functions. > > Note also that you can have struct-qualified properties (at least you should be able to, it seems Andrei's DIP does not allow it, but it's not at odds with UFCS). Those are just about equivalent to module-level properties, they just require specifying the struct name (and people have used this in clever ways to achieve good readable results). Hmm, no real difference to struct/class properties: import std.stdio; int i; void main() { int i; i=8; writeln(".i: ", .i); writeln(".i: ", testglobal.i); } Prints 0 both times. (The file was named testglobal.d) > It certainly is true. They can be treated like fields for getting and setting, and I think "syntactically" means you access them like you would access a field. > > You might say "yeah, but you can't do ++ on them!", but you can't do that to a const field either. Should const fields be considered fields? Well that's probably what was meant by this statement, but obviously it was misunderstood by me and many others. It suggests that you can use public fields if you would have required trivial set/get methods. (I even found a blog post by someone who was bashing against Java how silly they are and how cool D is, because we don't need trival set/get methods) > > A ref property pretty much behaves EXACTLY like a field. Exactly that is why I don't consider it a property. :-) > > > The current thinking instead seems to be that properties are just syntactic sugar and the level of encapsulation is business that is left to the developer. > > I don't think that SHOULD be D's business. Encapsulation should be possible, encouraged, but not dictated. It is not dictated, it is just encouraged and possible. If you don't need encapsulation, don't call it a property. Nothing changes. You even have a way out, if you first considered it a property with proper set/get and later on you decide that this is not really needed, you can drop it, no code will break, you just can not go back. But that was your decision the moment you gave up @property. > > > My thinking on the other hand is, that properties are the method of encapsulation, so they should ensure it. If something does not, it is not a property, which leads to a very clean design of properties. > > Such a design is entirely possible today, even if ref properties exist, even if *properties* didn't exist. Why must you use ref properties just because they are available? You don't, but their availability is no gain at all, because you would have exactly the same if you removed @property, just the concept of a property is screwed. If you want @property qualified ref returning functions, then you also want UFCS properties, and my point is that there is no point to it. > > Note that the reader/user of code is never guaranteed that they are working with properly encapsulated code. A property looks like a field, and a field is NOT encapsulated. So any compiler/language guarantees of encapsulation with properties are moot. > > -Steve My point is that it is wrong that a property should look like a field, this just causes confusion, because people mix it up and forget about encapsulation. Just stand to the fact that it is no field. If you want a field, use a field or a method returning ref, don't use @property. So there will be guaranteed to have a proper encapsulated field, the moment they see @property, but what is more important is, that the provider of the code can be sure that he/she got encapsulation right as long as he/she does qualifies something with @property. |
February 10, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert | On 02/10/2013 01:09 PM, Robert wrote:
> ...
>
> In my DIP a property is a function and they behave the same, except that
> prop=5; calls prop(5); if prop is a property.
>...
Why does this justify a keyword? I think Walter's initial proposal of getting rid of @property has more merit than this.
|
February 10, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Sun, 2013-02-10 at 13:40 +0100, Timon Gehr wrote:
> Why does this justify a keyword? I think Walter's initial proposal of getting rid of @property has more merit than this.
Read the DIP? It is about encapsulation and making set functions
callable with = in order to be compatible with ref returning functions:
(Compatible from set function to ref returning function, not the other
way round) and for the more expressive syntax:
a=something;
instead of
a(something);
and so that tools can easily extract what's a property. (For enabling access from scripting languages for example, like Qt does.)
The one reason why we can not drop it, is that = calls the set function on properties. The reason why we should not, is that having such a cool straight forward feature for providing proper no-boilerplate encapsulation seems valuable in an OOP enabled language.
|
February 10, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
> The reason why we should not, is that having such a cool straight forward feature for providing proper no-boilerplate encapsulation seems valuable in an OOP enabled language.
>
Provided we actually give properties this meaning in D, which is currently not the case.
|
February 10, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robert | On 02/10/2013 01:53 PM, Robert wrote: > On Sun, 2013-02-10 at 13:40 +0100, Timon Gehr wrote: >> Why does this justify a keyword? I think Walter's initial proposal of >> getting rid of @property has more merit than this. > > Read the DIP? Stop trolling. > It is about encapsulation Perfectly possible without DIP26 and encapsulation can be violated using @property as given in DIP26 just as easily as without it. > and making set functions > callable with = in order to be compatible with ref returning functions: > (Compatible from set function to ref returning function, not the other > way round) setter(2); > and for the more expressive syntax: > a=something; That's not more expressive. > instead of You mean as well as. > a(something); > > and so that tools can easily extract what's a property. (For enabling > access from scripting languages for example, like Qt does.) > Use UDAs. > The one reason why we can not drop it, is that = calls the set function > on properties. So does ( ). And both are the case already. > The reason why we should not, is that having such a cool > straight forward feature for providing proper no-boilerplate Boilerplate can be trivially automated. > encapsulation seems valuable in an OOP enabled language. > |
February 10, 2013 Re: Taking address of properties | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | > > > > Read the DIP? > > Stop trolling. Agreed, sorry. But by reading some arguments that are posted over and over again, I just get the feeling that people don't even read it, which is kinda frustrating, as it was a lot of work. > > > It is about encapsulation > > Perfectly possible without DIP26 and encapsulation can be violated using @property as given in DIP26 just as easily as without it. How?! I mean yeah, you can return a pointer to a field, that is correct. But apart from that, which might get forbidden too for properties (I have to think about it first), what do you mean? > > > and making set functions > > callable with = in order to be compatible with ref returning functions: > > (Compatible from set function to ref returning function, not the other > > way round) > > setter(2); Fine with me. What is the problem? > > > and for the more expressive syntax: > > a=something; > > That's not more expressive. If you want to state that the property should assume the given value, it is. Arguably not more than setA(something), but well it is a nice syntax and we stay compatible with ref returning functions and with the current implementation. > Use UDAs. True if this was the only reason. > > > The one reason why we can not drop it, is that = calls the set function on properties. > > So does ( ). And both are the case already. Yeah, which actually is a good thing, because we won't break much code. People have their code written according to the implementation not some specification, so if we keep most currently existing code running and get sane behaviour, I think this is a good thing, but ok there are people who seem to disagree. > > > The reason why we should not, is that having such a cool straight forward feature for providing proper no-boilerplate > > Boilerplate can be trivially automated. Also true, but integrating it into the language makes the property feature complete and well integrated. |
Copyright © 1999-2021 by the D Language Foundation