www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - property fields

reply Richard (Rikki) Andrew Cattermole <richard cattermole.co.nz> writes:
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
next sibling parent reply Dom DiSc <dominikus scherkl.de> writes:
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
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
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:
 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.
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.
 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
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
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:
 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.
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 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.
      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.
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.
 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.
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.
 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
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.
Dec 05
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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