digitalmars.dip.ideas - property fields
- Richard (Rikki) Andrew Cattermole (90/90) Dec 03 The ``@property`` attribute has been left in the lurch for quite
- Dom DiSc (10/15) Dec 04 Why do we need @property fields?
- Richard (Rikki) Andrew Cattermole (13/30) Dec 04 Yes, this has been discussed plenty in the past.
- Jonathan M Davis (45/102) Dec 05 If we were to do it, I would argue for just always making it private, si...
- Richard (Rikki) Andrew Cattermole (19/142) Dec 05 This makes operators work. Which is a very good default to have.
- Jonathan M Davis (21/29) Dec 05 IMHO, what should be done with @property is what SDC does, which is make...
The `` property`` attribute has been left in the lurch for quite some time, where only one behaviour related to its return value being of any value. There are two parts of this proposal, however I am only confident in the first part, fields. The other is for methods which would give you full control over it. Syntax is quite simple: `` property int field;`` It is valid in interfaces, structs, and classes. ```d interface IField { property int field; } class Thing : IField { property int field; } struct Thing2 { property int field; } ``` It results in three things. 1. A field that is either ``protected`` (classes) or ``private`` (structs). ```d class Thing : IField { protected int field; } struct Thing2 { private int field; } ``` If we have struct inheritance, then it would be ``protected`` instead. 2. It will generate a method for classes and structs in the form: ```d class Thing : IField { protected int field; ref typeof(this.field) __property_field() /* inferred attributes */ { return this.field; } } struct Thing2 { private int field; ref typeof(this.field) __property_field() /* inferred attributes */ { return this.field; } } ``` 3. A method stub is generated for the property field in an interface: ```d interface IField { ref typeof(this.field) __property_field(); } ``` You may place attributes around the field to apply to the method. ```d interface IField { safe: property int field; } ``` This allows an interface to require that a field exists, without defining one. Since it is the stub that gets inherited (which can only be implemented using an `` property`` field. Semantically: 1. You will not have direct access to the field outside of the type. All accesses and mutation will go through the method. 2. It may be passed to a function parameter that is ``ref`` or ``out``. This is perfectly safe so needs no protection due to it implicitly being ``scope``. 3. An invariant will be called following mutation or when it is passed to a ``ref`` or ``out`` function parameter. 3.1. For interfaces if a `` property`` field is used, an invariant will be required to be in the vtable implicitly. 4. In `` safe`` code: 4.1. It may not be stored in a ref variable ``ref var = thing.field;`` 4.2. It may not have a pointer taken to it ``&thing.field`` As long as the invariant is called after mutation, this covers the use case of setter methods for properties. It cannot be used to transform, but will cause errors. If you need transformation wrapping the field with a struct could introduce such behaviors so need not be provided at this level (although it should be supported with `` property`` methods).
Dec 03
On Tuesday, 3 December 2024 at 23:50:20 UTC, Richard (Rikki) Andrew Cattermole wrote:The `` property`` attribute has been left in the lurch for quite some time, where only one behaviour related to its return value being of any value. There are two parts of this proposal, however I am only confident in the first part, fields.Why do we need property fields? I mean, it's nice tho have the setter and getter beeing created automatically, but one of the important properties of property is to have control if you want only a getter (read only) or only a setter (write only), but here you get always both. Also, I mainly use property if I do NOT have a field, but the value is generated on the fly from other data (getter) or the input produces other effects/change multiple fields (setter).
Dec 04
On 05/12/2024 12:12 AM, Dom DiSc wrote:On Tuesday, 3 December 2024 at 23:50:20 UTC, Richard (Rikki) Andrew Cattermole wrote:Yes, this has been discussed plenty in the past. However, both behaviors can be implemented using a method yourself. Which is the reason I suspect for none of them being accepted. These setter and getter proposals have gone no where, partly because everything they do, can be done yourself. There are no new semantic behaviors provided. This proposal introduces new semantic behavior that can only be accessed via this syntax.The `` property`` attribute has been left in the lurch for quite some time, where only one behaviour related to its return value being of any value. There are two parts of this proposal, however I am only confident in the first part, fields.Why do we need property fields? I mean, it's nice tho have the setter and getter beeing created automatically, but one of the important properties of property is to have control if you want only a getter (read only) or only a setter (write only), but here you get always both.Also, I mainly use property if I do NOT have a field, but the value is generated on the fly from other data (getter) or the input produces other effects/change multiple fields (setter).What you are describing has nothing to do with the attribute. Anyway, no changes to existing methods are proposed (although there may still be an interaction so we may need to limit it to newer editions). So your previous usage will still work as it has.
Dec 04
On Tuesday, December 3, 2024 4:50:20 PM MST Richard Andrew Cattermole (Rikki) via dip.ideas wrote:The `` property`` attribute has been left in the lurch for quite some time, where only one behaviour related to its return value being of any value. There are two parts of this proposal, however I am only confident in the first part, fields. The other is for methods which would give you full control over it. Syntax is quite simple: `` property int field;`` It is valid in interfaces, structs, and classes. ```d interface IField { property int field; }class Thing : IField { property int field; } struct Thing2 { property int field; } ``` It results in three things. 1. A field that is either ``protected`` (classes) or ``private`` (structs).If we were to do it, I would argue for just always making it private, since that's better encapsulation, and while having protected fields certainly makes sense sometimes, it's not really the best default IMHO. Also, including interfaces in the mix seems like it would ultimately create confusion, since when you marked a variable with property in a class or struct, you'd get an actual variable, whereas with the interface, you wouldn't. It doesn't take much in the way of nuance like that to make it just clearer to write out the functions.2. It will generate a method for classes and structs in the form: ```d class Thing : IField { protected int field; ref typeof(this.field) __property_field() /* inferred attributes */ { return this.field; } }Returning by ref kind of defeats the purpose of property in general. _Sometimes_ it makes sense, but usually, if that's what you want, it makes more sense to just use a field and not bother with property functions at all. It also doesn't necessarily play nicely with inheritance, since it would be pretty easy to get into a situation where returning by ref made sense in some parts of the hierarchy but not in others, meaning that it couldn't return by ref in general. Certainly, returning by ref is something that would make more sense with a struct than a class. The other big problem with returning by ref is that the main reason to use property functions instead of just making the field public is so that you can swap out the implementation later without breaking the caller, whereas if the function returns by ref, they can take the address just like they could have with a public variable, which creates the same problem.This allows an interface to require that a field exists, without defining one. Since it is the stub that gets inherited (which can only be implemented using an `` property`` field.The could just as easily declare the property functions, and it wouldn't be much more typing. It would also make it explicit what the exact function signatures were.Semantically: 1. You will not have direct access to the field outside of the type. All accesses and mutation will go through the method. 2. It may be passed to a function parameter that is ``ref`` or ``out``. This is perfectly safe so needs no protection due to it implicitly being ``scope``. 3. An invariant will be called following mutation or when it is passed to a ``ref`` or ``out`` function parameter. 3.1. For interfaces if a `` property`` field is used, an invariant will be required to be in the vtable implicitly. 4. In `` safe`` code: 4.1. It may not be stored in a ref variable ``ref var = thing.field;`` 4.2. It may not have a pointer taken to it ``&thing.field`` As long as the invariant is called after mutation, this covers the use case of setter methods for properties. It cannot be used to transform, but will cause errors. If you need transformation wrapping the field with a struct could introduce such behaviors so need not be provided at this level (although it should be supported with `` property`` methods).There is _some_ value in wrapping a field in property functions that don't do anything when dealing with a public API, since then you can change the implementation without affecting the folks using the library, whereas swapping a public variable out for property functions would break such code. So, there probably are cases where having the compiler generate such boilerplate would be useful, but it's also not much more effort to type out the property functions. So, I don't think that the feature would pull its weight. Also, there are enough nuances with regards to what exactly the function signatures would look like and whether a variable would actually be created or not, that it feels to me like it's just doing too much which isn't immediately obvious. It can certainly be understood with some effort, but particularly if you're overriding these functions, you have to worry about exactly what's being generated, and ultimately, it's just going to be easier if you can see the actual function signatures and copy them. I just don't think that the little bit of boilerplate that this would provide is worth the cost, though I'm sure that others would disagree, since it's not the first time that a feature along these lines has been proposed. - Jonathan M Davis
Dec 05
On 06/12/2024 12:00 PM, Jonathan M Davis wrote:On Tuesday, December 3, 2024 4:50:20 PM MST Richard Andrew Cattermole (Rikki) via dip.ideas wrote:This makes operators work. Which is a very good default to have. If you want to do something else, you can define the methods manually.The `` property`` attribute has been left in the lurch for quite some time, where only one behaviour related to its return value being of any value. There are two parts of this proposal, however I am only confident in the first part, fields. The other is for methods which would give you full control over it. Syntax is quite simple: `` property int field;`` It is valid in interfaces, structs, and classes. ```d interface IField { property int field; }class Thing : IField { property int field; } struct Thing2 { property int field; } ``` It results in three things. 1. A field that is either ``protected`` (classes) or ``private`` (structs).If we were to do it, I would argue for just always making it private, since that's better encapsulation, and while having protected fields certainly makes sense sometimes, it's not really the best default IMHO. Also, including interfaces in the mix seems like it would ultimately create confusion, since when you marked a variable with property in a class or struct, you'd get an actual variable, whereas with the interface, you wouldn't. It doesn't take much in the way of nuance like that to make it just clearer to write out the functions.2. It will generate a method for classes and structs in the form: ```d class Thing : IField { protected int field; ref typeof(this.field) __property_field() /* inferred attributes */ { return this.field; } }Returning by ref kind of defeats the purpose of property in general. _Sometimes_ it makes sense, but usually, if that's what you want, it makes more sense to just use a field and not bother with property functions at all. It also doesn't necessarily play nicely with inheritance, since it would be pretty easy to get into a situation where returning by ref made sense in some parts of the hierarchy but not in others, meaning that it couldn't return by ref in general. Certainly, returning by ref is something that would make more sense with a struct than a class.The other big problem with returning by ref is that the main reason to use property functions instead of just making the field public is so that you can swap out the implementation later without breaking the caller, whereas if the function returns by ref, they can take the address just like they could have with a public variable, which creates the same problem.This isn't an issue here, you can swap it to a method later on and do something more manual.It could yes, but it wouldn't look like a field. This has come up before, and gives us an answer to how do you do a field in an interface.This allows an interface to require that a field exists, without defining one. Since it is the stub that gets inherited (which can only be implemented using an `` property`` field.The could just as easily declare the property functions, and it wouldn't be much more typing. It would also make it explicit what the exact function signatures were.And this is why I've stated that it must introduce new semantic behavior that cannot be done without it. I.e. guarantee invariant is called, cannot escape.Semantically: 1. You will not have direct access to the field outside of the type. All accesses and mutation will go through the method. 2. It may be passed to a function parameter that is ``ref`` or ``out``. This is perfectly safe so needs no protection due to it implicitly being ``scope``. 3. An invariant will be called following mutation or when it is passed to a ``ref`` or ``out`` function parameter. 3.1. For interfaces if a `` property`` field is used, an invariant will be required to be in the vtable implicitly. 4. In `` safe`` code: 4.1. It may not be stored in a ref variable ``ref var = thing.field;`` 4.2. It may not have a pointer taken to it ``&thing.field`` As long as the invariant is called after mutation, this covers the use case of setter methods for properties. It cannot be used to transform, but will cause errors. If you need transformation wrapping the field with a struct could introduce such behaviors so need not be provided at this level (although it should be supported with `` property`` methods).There is _some_ value in wrapping a field in property functions that don't do anything when dealing with a public API, since then you can change the implementation without affecting the folks using the library, whereas swapping a public variable out for property functions would break such code. So, there probably are cases where having the compiler generate such boilerplate would be useful, but it's also not much more effort to type out the property functions. So, I don't think that the feature would pull its weight.Also, there are enough nuances with regards to what exactly the function signatures would look like and whether a variable would actually be created or not, that it feels to me like it's just doing too much which isn't immediately obvious. It can certainly be understood with some effort, but particularly if you're overriding these functions, you have to worry about exactly what's being generated, and ultimately, it's just going to be easier if you can see the actual function signatures and copy them. I just don't think that the little bit of boilerplate that this would provide is worth the cost, though I'm sure that others would disagree, since it's not the first time that a feature along these lines has been proposed. - Jonathan M DavisJust to be clear, as far as I'm concerned, either this gets fully designed and implemented or it should be removed. We can't keep things like this in the language where its not doing anything useful for people (yes I know it does one thing that could be replaced with a trait). So if you have a proposal on how to make it worth keeping, please propose it. Otherwise I'm going to have to assume such push back is in favor of removal.
Dec 05
On Thursday, December 5, 2024 6:48:44 PM MST Richard (Rikki) Andrew Cattermole via dip.ideas wrote:Just to be clear, as far as I'm concerned, either this gets fully designed and implemented or it should be removed. We can't keep things like this in the language where its not doing anything useful for people (yes I know it does one thing that could be replaced with a trait). So if you have a proposal on how to make it worth keeping, please propose it. Otherwise I'm going to have to assume such push back is in favor of removal.IMHO, what should be done with property is what SDC does, which is make it an error to use the property like a function, which would allow us to have property functions which return delegates and other callables. That's what originally was going to be done with it (along with making it illegal to call non- property functions without parens), but since UFCS made it unacceptable to require that functions be called with parens (since many folks don't like having the parens with UFCS), we didn't do either even though we can still do the first part and require that property functions not use parens - particularly since that would fix an ambiguity problem that we currently have with property and parens. Either way, if it's a question of having property variables vs removal, I'd favor removal, because I don't see much value in the idea of property variables. But it's also the case that if we lost property, we'd have some problems with trait-related stuff, since there are cases where we take advantage of property's quirk of making the return type be the type given with is expressions in order to implement traits. Getting rid of property would actually break druntime and Phobos as things stand. - Jonathan M Davis
Dec 05