www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Using memberspaces for a property-like syntax and more

reply "TommiT" <tommitissari hotmail.com> writes:
I propose we just forget about the whole concept of a property, 
and instead add to D a new construct called 'memberspace'. 
Memberspace subdivides the 'space' in which the member functions 
and operators of a type live into separately named 'subspaces', 
like namespaces do for freestanding functions and classes in C++. 
Read more about the uses of this idea over there (it's not my 
idea):

http://accu.org/index.php/journals/1527

Here's how it could look like:

struct S
{
     int _value;

     memberspace space1
     {
         int opCall() const
         {
             return _value;
         }

         void opAssign(int v)
         {
             _value = v;
         }

         void method() const { }
     }

     memberspace space2
     {
         static int opCall()
         {
             return 123;
         }
     }
}

void main()
{
     S var;
     var.space1 = 42;
     assert(var.space1() == 42);
     assert(S.space2() == 123);
     var.space1.method();

     int n1 = var.space1; // error
     int n2 = S.space2;   // error
}

Now, imagine replacing the name of your memberspace with the name 
of the thing you used to call a 'property'.
Feb 02 2013
next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Saturday, 2 February 2013 at 09:14:30 UTC, TommiT wrote:
 I propose we just forget about the whole concept of a property, 
 and instead add to D a new construct called 'memberspace'. 
 Memberspace subdivides the 'space' in which the member 
 functions and operators of a type live into separately named 
 'subspaces', like namespaces do for freestanding functions and 
 classes in C++. Read more about the uses of this idea over 
 there (it's not my idea):

First impression: I don't know why but I don't like it. It feels like you're doing the following: 1) Declare a struct and immediately instantiate it with the same name. (Or union, or similar) 2) Only one declaration of this struct can exist, doesn't count as nested struct. 3) Disable all constructors/postblit/opAssign except as follows The only thing it has really done is allow you to force the use of parenthesis during a call (but only the get, not the set). You're then defining all opBinary/opAssign methods that it works with which seems like it will end up bloating the code considerably but not giving back as much as it takes to write it.
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 13:03:49 UTC, Era Scarecrow wrote:
  First impression: I don't know why but I don't like it.

  It feels like you're doing the following:

  1) Declare a struct and immediately instantiate it with the 
 same name. (Or union, or similar)
  2) Only one declaration of this struct can exist, doesn't 
 count as nested struct.
  3) Disable all constructors/postblit/opAssign except as follows

  [..]

Memberspaces shouldn't feel like variables at all. They should feel exactly like C++ namespaces that live inside a struct or a class. You can't take the address of a namespace or pass it as an argument. It's just an identifier that can be used to separate two same names, like std::sort and boost::sort. A nice IDE also colors memberspaces differently from a variables, etc. On Saturday, 2 February 2013 at 13:03:49 UTC, Era Scarecrow wrote:
  [..]
  The only thing it has really done is allow you to force the 
 use of parenthesis during a call (but only the get, not the 
 set).

I thought it was an unfortunate side-effect that my proposal doesn't give us the nice getter syntax: int n = var.prop; ...but I'm glad you like it (if I understood correctly). On Saturday, 2 February 2013 at 13:03:49 UTC, Era Scarecrow wrote:
 [..]
 You're then defining all opBinary/opAssign methods that it 
 works with which seems like it will end up bloating the code 
 considerably but not giving back as much as it takes to write 
 it.

But luckily in D you can write a whole bunch of operators at once by writing a restricted template operator, like: void opOpAssign(string op)(int rhs) if(op == "+" || op == "-" || op == "*" || op == "/") { mixin("_value" ~ op ~ "= rhs._value"); } But you're right in that memberspaces require a more explicit declaration of a property than most other property implementations. But those property proposals where you only specify a getter and a setter, albeit faster to write, they all suffer from a possible performance issue when the type returned by getter is non-trivial to copy. I.e. var.prop += 3; ...has to be done as: var.prop = var.prop + 3 Also, those other property implementations don't enable you to call the methods of your property in an encapsulated manner. E.g. if you property has a non-const set_to_invalid method, you can't encapsulate further in the struct/class where the property is defined. I.e: var.prop.set_to_invalid(); ...calls directly PropType.set_to_invalid without the type of 'var' having anything to say about it. That's not good for encapsulation.
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 13:03:49 UTC, Era Scarecrow wrote:
  First impression: I don't know why but I don't like it.
 [..]

Also, let's not miss the fact, that this proposal isn't really about properties anymore. It's about being able to divide your interface into multiple logical sections. It just so happens to be that this ability lends itself pretty nicely to implementing something that *looks* like a property. There's nothing that says you should use opCall as the getter of your "property" like this: var.prop() ...instead, it could use a method like: var.prop.get() ...and you could even have multiple getters: var.prop.get_regular() var.prop.get_filtered() I just thought that the use of opCall for 'properties' might be a good convention.
Feb 02 2013
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2013-14-02 10:02, TommiT <tommitissari hotmail.com> wrote:

 I propose we just forget about the whole concept of a property, and  
 instead add to D a new construct called 'memberspace'. Memberspace  
 subdivides the 'space' in which the member functions and operators of a  
 type live into separately named 'subspaces', like namespaces do for  
 freestanding functions and classes in C++. Read more about the uses of  
 this idea over there (it's not my idea):

 http://accu.org/index.php/journals/1527

 Here's how it could look like:

 struct S
 {
      int _value;

      memberspace space1
      {
          int opCall() const
          {
              return _value;
          }

          void opAssign(int v)
          {
              _value = v;
          }

          void method() const { }
      }

      memberspace space2
      {
          static int opCall()
          {
              return 123;
          }
      }
 }

 void main()
 {
      S var;
      var.space1 = 42;
      assert(var.space1() == 42);
      assert(S.space2() == 123);
      var.space1.method();

      int n1 = var.space1; // error
      int n2 = S.space2;   // error
 }

 Now, imagine replacing the name of your memberspace with the name of the  
 thing you used to call a 'property'.

This seems to be basically what I outlined in issue 5158[1]. [1] http://d.puremagic.com/issues/show_bug.cgi?id=5158 -- Simen
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 17:12:33 UTC, Simen Kjaeraas 
wrote:
 This seems to be basically what I outlined in issue 5158[1].


 [1] http://d.puremagic.com/issues/show_bug.cgi?id=5158

I don't understand your proposal. Are you saying you want the following code to compile? void main() { int a; void opAssign(int arg) {a = arg;} = 4; } And what would that code do? Also, how would you use your proposal to implement memberspaces?
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 18:37:11 UTC, TommiT wrote:
 On Saturday, 2 February 2013 at 17:12:33 UTC, Simen Kjaeraas 
 wrote:
 This seems to be basically what I outlined in issue 5158[1].


 [1] http://d.puremagic.com/issues/show_bug.cgi?id=5158

I don't understand your proposal. Are you saying you want the following code to compile? void main() { int a; void opAssign(int arg) {a = arg;} = 4; } And what would that code do? Also, how would you use your proposal to implement memberspaces?

I'm sorry, I mixed templates with template mixins. But I still don't understand your proposal, so if you could elaborate on it a bit more.
Feb 02 2013
prev sibling next sibling parent "Rob T" <alanb ucora.com> writes:
On Saturday, 2 February 2013 at 16:15:43 UTC, TommiT wrote:
 Also, let's not miss the fact, that this proposal isn't really 
 about properties anymore. It's about being able to divide your 
 interface into multiple logical sections. It just so happens to 
 be that this ability lends itself pretty nicely to implementing 
 something that *looks* like a property.

I agree. The previous property proposals suffer from being very specialized and not much of a useful extension to the language. We don't even need properties and one of the main goals, which is variable emulation is not even possible to implement so the best we'll get is partial emulation which prevents properties from being used as a drop in replacement for variables. If we can emulate properties (with the limitations that properties must have), with a general extension such as namespaces, then we're far better off. If we can use the namespace concept and implement properties in Phobos, then it shows that the concept is really useful and generalized enough for implementing other solutions besides only properties. --rt
Feb 02 2013
prev sibling next sibling parent "Rob T" <alanb ucora.com> writes:
On Saturday, 2 February 2013 at 15:56:41 UTC, TommiT wrote:
[..]
 Memberspaces shouldn't feel like variables at all. They should 
 feel exactly like C++ namespaces that live inside a struct or a 
 class. You can't take the address of a namespace or pass it as 
 an argument. It's just an identifier that can be used to 
 separate two same names, like std::sort and boost::sort. A nice 
 IDE also colors memberspaces differently from a variables, etc.

Rather than feel exactly like C++ namespaces, I am wondering if we could define a memberspace as a type so that can be reused multiple times in various areas of an application. With templates, a memberspace can be transformed in various ways to work with different data types. --rt
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 21:21:28 UTC, Rob T wrote:
 Rather than feel exactly like C++ namespaces, I am wondering if 
 we could define a memberspace as a type so that can be reused 
 multiple times in various areas of an application. With 
 templates, a memberspace can be transformed in various ways to 
 work with different data types.

 --rt

I think we can use template mixins to achieve that: mixin template add_stuff(alias var) { memberspace stuff { typeof(var) opCall() const { return var; } void opAssign(typeof(var) rhs) { var = rhs; } } } struct S { int _value; mixin add_stuff!(_value); } void main() { S s; s.stuff = 42; assert(s.stuff() == 42); }
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 21:21:28 UTC, Rob T wrote:
 On Saturday, 2 February 2013 at 15:56:41 UTC, TommiT wrote:
 [..]
 Memberspaces shouldn't feel like variables at all. They should 
 feel exactly like C++ namespaces that live inside a struct or 
 a class. [..]

Rather than feel exactly like C++ namespaces, [..]

Just to be clear, I didn't say/mean that memberspaces should feel exactly like C++ namespaces, nor is there any change that they would, given that in C++ namespaces can't live inside structs or classes and they use :: to separate them. Forgive me for explaining all that, in case you know C++ (I can't assume that everybody does).
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 21:39:45 UTC, TommiT wrote:
 I think we can use template mixins to achieve that:

 mixin template add_stuff(alias var)
 {
     memberspace stuff
     {
         typeof(var) opCall() const
         {
             return var;
         }

         void opAssign(typeof(var) rhs)
         {
             var = rhs;
         }
     }
 }

 struct S
 {
     int _value;

     mixin add_stuff!(_value);
 }

 void main()
 {
     S s;
     s.stuff = 42;
     assert(s.stuff() == 42);
 }

And here's how you could even add the name of your memberspace as one of the template mixin parameters: mixin template basic_property(alias prop, alias var) { memberspace prop { typeof(var) opCall() const { return var; } void opAssign(typeof(var) rhs) { var = rhs; } } } struct S { int _value; memberspace myprop { } mixin basic_property!(myprop, _value); } void main() { S s; s.myprop = 42; assert(s.myprop() == 42); }
Feb 02 2013
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 02, 2013 10:14:29 TommiT wrote:
 I propose we just forget about the whole concept of a property

I think that this proves that the property discussion has gotten way out of hand. The real question is whether we think that we need explicit properties or not. If we don't, then presumably, we'll end up with some variant of what Walter has suggestiing. If we do, then I see no reason to do anything other than just fixing up the implementation of property. The problems with it are almost entirely due to a lack of being properly implemented rather than there being a problem with its design. Really, the only major design issue with property that's been being debated is whether it should make it so that parens are required on normal function calls, and everything else about property is actually indepedent of that. I don't think that we should be trying to add major, new concepts right now. I think that we should just decide whether or not we need explicit properties. And that will decide whether we go with a variant of what Walter is suggesting or whether we do something with property. All these other suggestions are just complicating things to no real benefit. - Jonathan M Davis
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 22:18:52 UTC, Jonathan M Davis 
wrote:
 On Saturday, February 02, 2013 10:14:29 TommiT wrote:
 I propose we just forget about the whole concept of a property

I think that this proves that the property discussion has gotten way out of hand.

I don't see that quoted sentence of mine as a proof that the discussion has gotten out of hand. It only shows that I personally think that memberspaces are semantically such a close match to the concept of properties, that I don't see any reason to think of them as different things. BTW, given that we fix the property attribute, would the line: s.prop += 3; ...call: 1) s.prop( s.prop + 3 ); ...or: 2) s.prop().opOpAssign!"+"(3); ...in the code snippet below: struct T { private int _value; ref T opAssign(int rhs) { _value = rhs; return this; } ref T opOpAssign(string op)(int rhs) if(op == "+" || op == "-") { mixin("_value" ~ op ~ "= rhs;"); return this; } T opBinary(string op)(int rhs) const if(op == "+" || op == "-") { T t = this; mixin("t._value" ~ op ~ "= rhs;"); return t; } } struct S { private T _t; property ref T prop() { return _t; } property void prop(int v) { _t = v; } } void main() { S s; s.prop += 3; // ? }
Feb 02 2013
prev sibling next sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Saturday, 2 February 2013 at 22:18:52 UTC, Jonathan M Davis 
wrote:
 [..] All these other suggestions are
 just complicating things to no real benefit.

You say "there's no real benefit". I say "there's the benefit of being able to add another layer of encapsulation when it's needed". Here's what I mean by that: The lack of encapsulation with property attribute: struct T { private int _value; void add_twice(int v) { _value += 2 * v; } } struct S { private T _t; private int _sum_of_squares; // Can't update this property ref T prop() { return _t; } } void main() { S s; s.prop.add_twice(); // *Not* incrementing s._sum_of_squares } ...Whereas with memberspaces we can add that extra layer of encapsulation: struct S { private T _t; private int _sum_of_squares; memberspace prop { ref const(T) opCall() const { return _t; } void add_twice(int v) { _sum_of_squares += (2 * v)^2; // Yippee! return _t.add_twice(v); } } }
Feb 02 2013
prev sibling next sibling parent "Rob T" <alanb ucora.com> writes:
On Saturday, 2 February 2013 at 22:18:52 UTC, Jonathan M Davis 
wrote:
 I don't think that we should be trying to add major, new 
 concepts right now. I
 think that we should just decide whether or not we need 
 explicit properties.
 And that will decide whether we go with a variant of what 
 Walter is suggesting
 or whether we do something with  property. All these other 
 suggestions are
 just complicating things to no real benefit.

 - Jonathan M Davis

property is a dead end IMHO because it adds little to the language and can never be used to emulate real variables. The memberspace concept does at least as well as property can ever do and is a more generalized solution that can be used beyond just implementing properties. Why bother with property when we can much better? --rt
Feb 02 2013
prev sibling next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Saturday, 2 February 2013 at 23:23:37 UTC, TommiT wrote:
 You say "there's no real benefit". I say "there's the benefit 
 of being able to add another layer of encapsulation when it's 
 needed". Here's what I mean by that:

 The lack of encapsulation with  property attribute:

 struct T
 {
     private int _value;

     void add_twice(int v)
     {
         _value += 2 * v;
     }
 }

 struct S
 {
     private T _t;
     private int _sum_of_squares; // Can't update this

      property ref T prop()
     {
         return _t;
     }
 }

 void main()
 {
     S s;
     s.prop.add_twice(); // *Not* incrementing s._sum_of_squares
 }

I'd just call that bad design, I wouldn't reference a variable like that unless it's returned const or I don't care about if anything else accesses it. Since T/_t is private you shouldn't be giving access to them without a good reason.
 ...Whereas with memberspaces we can add that extra layer of 
 encapsulation:

 struct S
 {
     private T _t;
     private int _sum_of_squares;

     memberspace prop
     {
         ref const(T) opCall() const
         {
             return _t;
         }

So this one's allowed a const get return....
         void add_twice(int v)
         {
             _sum_of_squares += (2 * v)^2; // Yippee!
             return _t.add_twice(v);
         }
     }
 }

Hmmm curiously the above previous example didn't include add_twice outside of T. had it of and had a const get I'm sure these would be about the same. I'll your example(s) here reflect a bad/different design rather than why namespaces should be used. Besides, wasn't namespaces in D handled due to initial modular setup and alias?
Feb 02 2013
prev sibling parent "TommiT" <tommitissari hotmail.com> writes:
On Sunday, 3 February 2013 at 01:45:14 UTC, Era Scarecrow wrote:
 On Saturday, 2 February 2013 at 23:23:37 UTC, TommiT wrote:
 You say "there's no real benefit". I say "there's the benefit 
 of being able to add another layer of encapsulation when it's 
 needed". Here's what I mean by that:

 The lack of encapsulation with  property attribute:

 struct T
 {
    private int _value;

    void add_twice(int v)
    {
        _value += 2 * v;
    }
 }

 struct S
 {
    private T _t;
    private int _sum_of_squares; // Can't update this

     property ref T prop()
    {
        return _t;
    }
 }

 void main()
 {
    S s;
    s.prop.add_twice(); // *Not* incrementing s._sum_of_squares
 }

I'd just call that bad design, I wouldn't reference a variable like that unless it's returned const or I don't care about if anything else accesses it. Since T/_t is private you shouldn't be giving access to them without a good reason.
 ...Whereas with memberspaces we can add that extra layer of 
 encapsulation:

 struct S
 {
    private T _t;
    private int _sum_of_squares;

    memberspace prop
    {
        ref const(T) opCall() const
        {
            return _t;
        }

So this one's allowed a const get return....
        void add_twice(int v)
        {
            _sum_of_squares += (2 * v)^2; // Yippee!
            return _t.add_twice(v);
        }
    }
 }

Hmmm curiously the above previous example didn't include add_twice outside of T. had it of and had a const get I'm sure these would be about the same. I'll your example(s) here reflect a bad/different design rather than why namespaces should be used.

I agree that the example of mine which used property attribute is a bad design, and that's exactly my point. The point is that it's impossible to create a good design of 'S' with property attribute, if the interface is required to have a property-like syntax. And if the interface isn't required to provide a property-like syntax, then what are we even talking about? In order to implement the same level of encapsulation as there is in the example of mine which uses memberspaces, you'd probably prefix the method names with the name of the property to make a hint to the user that those things are linked to each other. That indicates a failure in what property attribute tries to accomplish. Anyway, it would look something like this: struct S { private T _t; private int _sum_of_squares; property ref const(T) prop() const { return _t; } // This method here shows that our attempts at creating // property semantics with property attribute has failed: void prop_add_twice(int v) { _sum_of_squares += (2 * v)^^2; _t.add_twice(v); } } On Sunday, 3 February 2013 at 01:45:14 UTC, Era Scarecrow wrote:
  Besides, wasn't namespaces in D handled due to initial modular 
 setup and alias?

Module-level and type-level are two different things.
Feb 02 2013