digitalmars.D - ref returns and properties
- Andrei Alexandrescu (37/37) Jan 25 2009 I realized that properties are not a complete model for actual data. The...
- Michiel Helvensteijn (21/38) Jan 25 2009 I posted a possible solution to this (I think) in the last properties
- Andrei Alexandrescu (18/53) Jan 25 2009 But these are too many. These should suffice:
- Jarrett Billingsley (8/24) Jan 25 2009 I absolutely wouldn't. Once we start doing stuff like that, we start
- Andrei Alexandrescu (6/34) Jan 25 2009 But you lose correctness and efficiency. I agree that in certain cases
- Denis Koroskin (2/36) Jan 25 2009 get/set/free?
- Andrei Alexandrescu (3/48) Jan 25 2009 With these you can't move a resource inside the property.
- Daniel Keep (7/15) Jan 25 2009 Python has an overload for removing properties. In all my years of
- Sean Kelly (3/18) Jan 26 2009 Does Python have complex value types?
- Daniel Keep (6/28) Jan 26 2009 (-4+7j)
- Andrei Alexandrescu (27/57) Jan 26 2009 He meant the latter (emphasis on the other syllable, I always forget
- Sergey Gromov (6/27) Jan 26 2009 This will happen in C++, too, if operator*() decides to throw.
- Andrei Alexandrescu (6/35) Jan 26 2009 Of course. The problem with get and set is not that they make it
- dsimcha (5/33) Jan 26 2009 Using j instead of i is pretty common in electrical engineering circles....
- John Reimer (7/51) Jan 26 2009 Right, the 'j' notation is used in the representation of phasors in ac c...
- Jarrett Billingsley (4/6) Jan 25 2009 Correctness is fine, but efficiency be damned. Look at where _that_
- Andrei Alexandrescu (5/13) Jan 25 2009 Not sure that efficiency is the source of most or even many of the known...
- Jacob Carlborg (18/79) Jan 26 2009 What about this:
- Andrei Alexandrescu (24/45) Jan 26 2009 I was hoping I'd shield putative users from having to write verbose
- Don (16/68) Jan 26 2009 I'm starting to wonder if we need some restrictions on fields, in order
- Sean Kelly (8/60) Jan 26 2009 I'm hesitant to assert that it should be illegal to take the address of
- Don (7/70) Jan 27 2009 At least, it's something you could do in a lint tool. I think it would
- Yigal Chripun (6/86) Jan 26 2009 I agree with Daniel and I think that 99.999% of users will use
- Steven Schveighoffer (61/98) Jan 26 2009 There are two possibilities that I can think of.
- Andrei Alexandrescu (22/81) Jan 26 2009 Yah, exactly. One thing I'm rather sad about is that certain containers
- Steven Schveighoffer (17/49) Jan 26 2009 A good point. Let's not forget that the main purpose of ranges being
- Andrei Alexandrescu (18/29) Jan 26 2009 Say you have an object obj that has a Matrix property and you have a
- Steven Schveighoffer (16/46) Jan 26 2009 So what you are saying is that you want to control the set such that it
- Denis Koroskin (16/80) Jan 26 2009 Because set implies data copying (read: allocation) and copy constructor...
- Steven Schveighoffer (17/113) Jan 26 2009 OK, I think I got what you and Andrei are saying. I had to think about ...
I realized that properties are not a complete model for actual data. The current conventional wisdom about properties goes like this (T is some type): class Host { property prop { T get() { ... } void set(T value) { ... } } } If T is e.g. an int, it all works nicely. Now consider T is a highly-structured piece of data that holds resources. In that case we want to make sure T is not copied unwittingly such that the resource is managed properly. This means that T has a copy constructor and a destructor. For such cases, extensive experience with C++ has shown that two primitives are essential: move and swap. void move(ref T src, ref T dst); void swap(ref T lhs, ref T rhs); Move takes the guts of src, puts them in dst, and then clears src effectively relieving it from any resource. Swap exchanges the guts of src and dst without any extra resource copying. Now if "prop" were a classic data, swapping host1.prop and host2.prop is a piece of cake (that is realized inside std.algorithm. Look it up, the implementation has quite a few interesting quirks!) But if "prop" is a property with get and set, everything falls apart. Properties effectively hide the address of the actual data and only traffic in values. That's often good (and sometimes even the only possibility in the case of properties computed on-the-fly), but this abstraction effectively makes resource-conserving move and swap impossible. I noticed this problem when dealing with ranges. The .head() function, if it returns a value, cannot be swapped/moved. Same applies to opIndex when implemented as a value property (via the opIndex/opIndexAssign tandem). What to do? I'd like to refine the notion of property such that moving properties around is possible, without, however, complicating their interface too much. Andrei
Jan 25 2009
Andrei Alexandrescu wrote:void move(ref T src, ref T dst); void swap(ref T lhs, ref T rhs); ... But if "prop" is a property with get and set, everything falls apart. Properties effectively hide the address of the actual data and only traffic in values. That's often good (and sometimes even the only possibility in the case of properties computed on-the-fly), but this abstraction effectively makes resource-conserving move and swap impossible. ... What to do? I'd like to refine the notion of property such that moving properties around is possible, without, however, complicating their interface too much.I posted a possible solution to this (I think) in the last properties thread. I was thinking that properties might be equiped with extra member functions. One advantage is that you might get more efficiency. I think you just named another. class Host { property prop { T get() { ... } void set(T value) { ... } void move(ref T dst) { ... } void swap(ref T rhs) { ... } } } Of course, in my idea (and my programming language), there is no difference between f(a, b) and a.f(b), so that automatically works out. Not sure how that would work out in D. One possibility is to just live with Host.prop.move(dst). -- Michiel
Jan 25 2009
Michiel Helvensteijn wrote:Andrei Alexandrescu wrote:But these are too many. These should suffice: class Host { property prop { T get(); void acquire(ref T value) { ... } void release(ref T value) { ... } } } set() can be implemented as acquire from a copy, and swap can be implemented by calls to acquire and release. Technically, get() could be also implemented with acquire and release, but that's too intensive for most types. The problem I'm seeing is that most people won't want to go through the trouble of implementing three functions for one property. Andreivoid move(ref T src, ref T dst); void swap(ref T lhs, ref T rhs); ... But if "prop" is a property with get and set, everything falls apart. Properties effectively hide the address of the actual data and only traffic in values. That's often good (and sometimes even the only possibility in the case of properties computed on-the-fly), but this abstraction effectively makes resource-conserving move and swap impossible. ... What to do? I'd like to refine the notion of property such that moving properties around is possible, without, however, complicating their interface too much.I posted a possible solution to this (I think) in the last properties thread. I was thinking that properties might be equiped with extra member functions. One advantage is that you might get more efficiency. I think you just named another. class Host { property prop { T get() { ... } void set(T value) { ... } void move(ref T dst) { ... } void swap(ref T rhs) { ... } } }
Jan 25 2009
On Sun, Jan 25, 2009 at 3:00 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:But these are too many. These should suffice: class Host { property prop { T get(); void acquire(ref T value) { ... } void release(ref T value) { ... } } } set() can be implemented as acquire from a copy, and swap can be implemented by calls to acquire and release. Technically, get() could be also implemented with acquire and release, but that's too intensive for most types. The problem I'm seeing is that most people won't want to go through the trouble of implementing three functions for one property.I absolutely wouldn't. Once we start doing stuff like that, we start getting into tedious C++ territory. Holding the compiler's hand, writing mountains of tedious, easy-to-mess-up boilerplate code, just to be able to implement simple things so they fit in with the language's weird semantics. At that point I'd _rather_ use the current "properties."
Jan 25 2009
Jarrett Billingsley wrote:On Sun, Jan 25, 2009 at 3:00 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:But you lose correctness and efficiency. I agree that in certain cases they aren't paramount, but you can't build a language to not allow them. A possible solution would be to require only get, make acquire necessary only for writable properties, and make release entirely optional. AndreiBut these are too many. These should suffice: class Host { property prop { T get(); void acquire(ref T value) { ... } void release(ref T value) { ... } } } set() can be implemented as acquire from a copy, and swap can be implemented by calls to acquire and release. Technically, get() could be also implemented with acquire and release, but that's too intensive for most types. The problem I'm seeing is that most people won't want to go through the trouble of implementing three functions for one property.I absolutely wouldn't. Once we start doing stuff like that, we start getting into tedious C++ territory. Holding the compiler's hand, writing mountains of tedious, easy-to-mess-up boilerplate code, just to be able to implement simple things so they fit in with the language's weird semantics. At that point I'd _rather_ use the current "properties."
Jan 25 2009
On Mon, 26 Jan 2009 00:59:32 +0300, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Jarrett Billingsley wrote:get/set/free?On Sun, Jan 25, 2009 at 3:00 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:But you lose correctness and efficiency. I agree that in certain cases they aren't paramount, but you can't build a language to not allow them. A possible solution would be to require only get, make acquire necessary only for writable properties, and make release entirely optional. AndreiBut these are too many. These should suffice: class Host { property prop { T get(); void acquire(ref T value) { ... } void release(ref T value) { ... } } } set() can be implemented as acquire from a copy, and swap can be implemented by calls to acquire and release. Technically, get() could be also implemented with acquire and release, but that's too intensive for most types. The problem I'm seeing is that most people won't want to go through the trouble of implementing three functions for one property.I absolutely wouldn't. Once we start doing stuff like that, we start getting into tedious C++ territory. Holding the compiler's hand, writing mountains of tedious, easy-to-mess-up boilerplate code, just to be able to implement simple things so they fit in with the language's weird semantics. At that point I'd _rather_ use the current "properties."
Jan 25 2009
Denis Koroskin wrote:On Mon, 26 Jan 2009 00:59:32 +0300, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:With these you can't move a resource inside the property. AndreiJarrett Billingsley wrote:get/set/free?On Sun, Jan 25, 2009 at 3:00 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:But you lose correctness and efficiency. I agree that in certain cases they aren't paramount, but you can't build a language to not allow them. A possible solution would be to require only get, make acquire necessary only for writable properties, and make release entirely optional. AndreiBut these are too many. These should suffice: class Host { property prop { T get(); void acquire(ref T value) { ... } void release(ref T value) { ... } } } set() can be implemented as acquire from a copy, and swap can be implemented by calls to acquire and release. Technically, get() could be also implemented with acquire and release, but that's too intensive for most types. The problem I'm seeing is that most people won't want to go through the trouble of implementing three functions for one property.I absolutely wouldn't. Once we start doing stuff like that, we start getting into tedious C++ territory. Holding the compiler's hand, writing mountains of tedious, easy-to-mess-up boilerplate code, just to be able to implement simple things so they fit in with the language's weird semantics. At that point I'd _rather_ use the current "properties."
Jan 25 2009
Andrei Alexandrescu wrote:Denis Koroskin wrote:Python has an overload for removing properties. In all my years of using Python, I've *NEVER* once had a use for it, or even worked out why I'd want to use it. I'm not saying we shouldn't be able to do this, I just can't see the need for move/remove for properties; where would this be useful? -- Daniel[snip] get/set/free?With these you can't move a resource inside the property. Andrei
Jan 25 2009
Daniel Keep wrote:Andrei Alexandrescu wrote:Does Python have complex value types? SeanDenis Koroskin wrote:Python has an overload for removing properties. In all my years of using Python, I've *NEVER* once had a use for it, or even worked out why I'd want to use it. I'm not saying we shouldn't be able to do this, I just can't see the need for move/remove for properties; where would this be useful?[snip] get/set/free?With these you can't move a resource inside the property. Andrei
Jan 26 2009
Sean Kelly wrote:Daniel Keep wrote:You mean these?Andrei Alexandrescu wrote:Does Python have complex value types? SeanDenis Koroskin wrote:Python has an overload for removing properties. In all my years of using Python, I've *NEVER* once had a use for it, or even worked out why I'd want to use it. I'm not saying we shouldn't be able to do this, I just can't see the need for move/remove for properties; where would this be useful?[snip] get/set/free?With these you can't move a resource inside the property. Andrei(-4+7j) Don't ask me why they used 'j' instead of 'i'. :P If you mean aggregate types that have value semantics, then no. -- Daniel(1+2j) * (2+3j)
Jan 26 2009
Daniel Keep wrote:Sean Kelly wrote:He meant the latter (emphasis on the other syllable, I always forget which is which). This brings back the notion of precision and efficiency. C++ tries to define operations such that some never throw, as those can be used in code with transactional semantics. It also tries to manage resources deterministically, which means there's strict control over values being created and destroyed. It's hard to combine these two within a harmonious system. Anyhow, here's a simple D example. Consider we define a BigInt type as a value-type struct: when you copy a BigInt to another, the latter becomes an independent copy: BigInt a = 100; BigInt b = a; ++b; assert(a == 100); BigInt's copy constructor would allocate memory dynamically, which means it may throw and also that it is inefficient to copy BigInt objects unwittingly. So far, so good. Now say we define some range that iterates over BigInts. If that range chooses to implement head() as a property, then a copy is created whenever you ask for head. The small problem is that that's inefficient. The larger problem is that there is no way to correctly e.g. sort such a range. Sorting hinges on swap, and with properties you can't ever swap without risking to throw. Sort would end up throwing, and not only throwing, but losing state irretrievably while at it. Well that's not a foundation we want to build D on, do we? AndreiDaniel Keep wrote:You mean these?Andrei Alexandrescu wrote:Does Python have complex value types? SeanDenis Koroskin wrote:Python has an overload for removing properties. In all my years of using Python, I've *NEVER* once had a use for it, or even worked out why I'd want to use it. I'm not saying we shouldn't be able to do this, I just can't see the need for move/remove for properties; where would this be useful?[snip] get/set/free?With these you can't move a resource inside the property. Andrei(-4+7j) Don't ask me why they used 'j' instead of 'i'. :P If you mean aggregate types that have value semantics, then no.(1+2j) * (2+3j)
Jan 26 2009
Mon, 26 Jan 2009 08:06:05 -0800, Andrei Alexandrescu wrote:Anyhow, here's a simple D example. Consider we define a BigInt type as a value-type struct: when you copy a BigInt to another, the latter becomes an independent copy: BigInt a = 100; BigInt b = a; ++b; assert(a == 100); BigInt's copy constructor would allocate memory dynamically, which means it may throw and also that it is inefficient to copy BigInt objects unwittingly. So far, so good. Now say we define some range that iterates over BigInts. If that range chooses to implement head() as a property, then a copy is created whenever you ask for head. The small problem is that that's inefficient. The larger problem is that there is no way to correctly e.g. sort such a range. Sorting hinges on swap, and with properties you can't ever swap without risking to throw. Sort would end up throwing, and not only throwing, but losing state irretrievably while at it. Well that's not a foundation we want to build D on, do we?This will happen in C++, too, if operator*() decides to throw. Algorithms are correct only if objects they manipulate obey the rules. It seems like your rule is, 'forward ranges are nothrow.' Any senior ranges included. Or maybe throwing next() or empty() are less dangerous?
Jan 26 2009
Sergey Gromov wrote:Mon, 26 Jan 2009 08:06:05 -0800, Andrei Alexandrescu wrote:Of course. The problem with get and set is not that they make it possible to write incorrect code. They make it impossible to write correct code.Anyhow, here's a simple D example. Consider we define a BigInt type as a value-type struct: when you copy a BigInt to another, the latter becomes an independent copy: BigInt a = 100; BigInt b = a; ++b; assert(a == 100); BigInt's copy constructor would allocate memory dynamically, which means it may throw and also that it is inefficient to copy BigInt objects unwittingly. So far, so good. Now say we define some range that iterates over BigInts. If that range chooses to implement head() as a property, then a copy is created whenever you ask for head. The small problem is that that's inefficient. The larger problem is that there is no way to correctly e.g. sort such a range. Sorting hinges on swap, and with properties you can't ever swap without risking to throw. Sort would end up throwing, and not only throwing, but losing state irretrievably while at it. Well that's not a foundation we want to build D on, do we?This will happen in C++, too, if operator*() decides to throw. Algorithms are correct only if objects they manipulate obey the rules.It seems like your rule is, 'forward ranges are nothrow.' Any senior ranges included. Or maybe throwing next() or empty() are less dangerous?Yes, because even if they throw, net loss of state is still avoidable. Andrei
Jan 26 2009
== Quote from Daniel Keep (daniel.keep.lists gmail.com)'s articleSean Kelly wrote:Using j instead of i is pretty common in electrical engineering circles. i means current when dealing with circuits, and complex numbers are used all over the place to make the math easier when doing circuit stuff, so by convention j is used instead of i to denote imaginary numbers.Daniel Keep wrote:You mean these?Andrei Alexandrescu wrote:Does Python have complex value types? SeanDenis Koroskin wrote:Python has an overload for removing properties. In all my years of using Python, I've *NEVER* once had a use for it, or even worked out why I'd want to use it. I'm not saying we shouldn't be able to do this, I just can't see the need for move/remove for properties; where would this be useful?[snip] get/set/free?With these you can't move a resource inside the property. Andrei(-4+7j) Don't ask me why they used 'j' instead of 'i'. :P If you mean aggregate types that have value semantics, then no. -- Daniel(1+2j) * (2+3j)
Jan 26 2009
Hello dsimcha,== Quote from Daniel Keep (daniel.keep.lists gmail.com)'s articleRight, the 'j' notation is used in the representation of phasors in ac circuit analysis. If I remember correctly, 'i' , usually represented as a function of time, is the changing current (like in a sinusoidal waveform)... and 'I' is the instantaneous current. I used to do a lot of phasor calculation practice. :-P -JJRSean Kelly wrote:Using j instead of i is pretty common in electrical engineering circles. i means current when dealing with circuits, and complex numbers are used all over the place to make the math easier when doing circuit stuff, so by convention j is used instead of i to denote imaginary numbers.Daniel Keep wrote:You mean these?Andrei Alexandrescu wrote:Does Python have complex value types? SeanDenis Koroskin wrote:Python has an overload for removing properties. In all my years of using Python, I've *NEVER* once had a use for it, or even worked out why I'd want to use it. I'm not saying we shouldn't be able to do this, I just can't see the need for move/remove for properties; where would this be useful?[snip] get/set/free?With these you can't move a resource inside the property. Andrei(-4+7j) Don't ask me why they used 'j' instead of 'i'. :P If you mean aggregate types that have value semantics, then no. -- Daniel(1+2j) * (2+3j)
Jan 26 2009
On Sun, Jan 25, 2009 at 4:59 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:But you lose correctness and efficiency. I agree that in certain cases they aren't paramount, but you can't build a language to not allow them.Correctness is fine, but efficiency be damned. Look at where _that_ mantra has gotten C++.
Jan 25 2009
Jarrett Billingsley wrote:On Sun, Jan 25, 2009 at 4:59 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:You and I don't seem to frequent the same circles.But you lose correctness and efficiency. I agree that in certain cases they aren't paramount, but you can't build a language to not allow them.Correctness is fine, but efficiency be damned.Look at where _that_ mantra has gotten C++.Not sure that efficiency is the source of most or even many of the known issues with C++. Andrei
Jan 25 2009
Andrei Alexandrescu wrote:Michiel Helvensteijn wrote:What about this: class Host { property acquire release prop { T get(); } } And then the compiler will automatically created the "acquire" and "release" methods. There could also be what I would like to call "property shortcuts" (ruby calls this attributes) like this: class Host { get acquire release T prop; get set T prop2; }Andrei Alexandrescu wrote:But these are too many. These should suffice: class Host { property prop { T get(); void acquire(ref T value) { ... } void release(ref T value) { ... } } } set() can be implemented as acquire from a copy, and swap can be implemented by calls to acquire and release. Technically, get() could be also implemented with acquire and release, but that's too intensive for most types. The problem I'm seeing is that most people won't want to go through the trouble of implementing three functions for one property. Andreivoid move(ref T src, ref T dst); void swap(ref T lhs, ref T rhs); ... But if "prop" is a property with get and set, everything falls apart. Properties effectively hide the address of the actual data and only traffic in values. That's often good (and sometimes even the only possibility in the case of properties computed on-the-fly), but this abstraction effectively makes resource-conserving move and swap impossible. ... What to do? I'd like to refine the notion of property such that moving properties around is possible, without, however, complicating their interface too much.I posted a possible solution to this (I think) in the last properties thread. I was thinking that properties might be equiped with extra member functions. One advantage is that you might get more efficiency. I think you just named another. class Host { property prop { T get() { ... } void set(T value) { ... } void move(ref T dst) { ... } void swap(ref T rhs) { ... } } }
Jan 26 2009
Jacob Carlborg wrote: [on properties]What about this: class Host { property acquire release prop { T get(); } } And then the compiler will automatically created the "acquire" and "release" methods. There could also be what I would like to call "property shortcuts" (ruby calls this attributes) like this: class Host { get acquire release T prop; get set T prop2; }I was hoping I'd shield putative users from having to write verbose code. Besides, there's one other issue I stumbled upon while working on porting std.algorithm to ranges. You can't really pass an entire property to a function. This furthers the rift between properties and true fields. Consider: struct A { int x; } void foo(ref int n) { if (n == 0) n = 1; } ... A obj; foo(obj.x); So when passing obj.x to foo, foo can read and write it no problem. But if x is a property of A, it all falls apart: obj.x means just reading the property, not getting the property with all of its get and set splendor. How did this hit me in std.algorithm? Replace A with your range of choice, x with head, and foo with swap. If head is implemented as a property instead of a ref-returning function or a field, I can't swap the heads of two ranges!! This limits how ranges can be implemented; I was hoping to allow head as a property, but it looks like I can't. Same goes about opIndex. If you define a random-access range, you can't define opIndex and opIndexAssign. You must define opIndex to return a reference. That's a bummer. Andrei
Jan 26 2009
Andrei Alexandrescu wrote:Jacob Carlborg wrote: [on properties]I'm starting to wonder if we need some restrictions on fields, in order to make properties and fields interchangable. The idea that a field could eventually be completely replaceable with functions is appealing, but I think it's only possible with a huge performance hit. One can distinguish between POD fields (for which any operation is legal) and property fields (which you cannot take the address of, for example). Arguably classes should normally not contain public POD fields, only public property fields. Perhaps this is a useful concept. Incidentally, a simpler way of bridging the divide would have been to drop property syntax and instead allow public fields to be accessed using function notation. Setting would be similar to C++ constructors: int z = obj.x() + 7; obj.x(6);What about this: class Host { property acquire release prop { T get(); } } And then the compiler will automatically created the "acquire" and "release" methods. There could also be what I would like to call "property shortcuts" (ruby calls this attributes) like this: class Host { get acquire release T prop; get set T prop2; }I was hoping I'd shield putative users from having to write verbose code. Besides, there's one other issue I stumbled upon while working on porting std.algorithm to ranges. You can't really pass an entire property to a function. This furthers the rift between properties and true fields. Consider: struct A { int x; } void foo(ref int n) { if (n == 0) n = 1; } ... A obj; foo(obj.x); So when passing obj.x to foo, foo can read and write it no problem. But if x is a property of A, it all falls apart: obj.x means just reading the property, not getting the property with all of its get and set splendor.How did this hit me in std.algorithm? Replace A with your range of choice, x with head, and foo with swap. If head is implemented as a property instead of a ref-returning function or a field, I can't swap the heads of two ranges!! This limits how ranges can be implemented; I was hoping to allow head as a property, but it looks like I can't. Same goes about opIndex. If you define a random-access range, you can't define opIndex and opIndexAssign. You must define opIndex to return a reference. That's a bummer.Yuck.Andrei
Jan 26 2009
Don wrote:Andrei Alexandrescu wrote:I'm hesitant to assert that it should be illegal to take the address of a public data member, but this is certainly an intriguing idea. The only other option I can think of would be to make swap a built-in property of concrete types and expecting the user to call a.swap(b), though it seems weird that a.swap(b) couldn't be rewritten as swap(a,b) for any concrete type, regardless of context. SeanJacob Carlborg wrote: [on properties]I'm starting to wonder if we need some restrictions on fields, in order to make properties and fields interchangable. The idea that a field could eventually be completely replaceable with functions is appealing, but I think it's only possible with a huge performance hit. One can distinguish between POD fields (for which any operation is legal) and property fields (which you cannot take the address of, for example). Arguably classes should normally not contain public POD fields, only public property fields. Perhaps this is a useful concept.What about this: class Host { property acquire release prop { T get(); } } And then the compiler will automatically created the "acquire" and "release" methods. There could also be what I would like to call "property shortcuts" (ruby calls this attributes) like this: class Host { get acquire release T prop; get set T prop2; }I was hoping I'd shield putative users from having to write verbose code. Besides, there's one other issue I stumbled upon while working on porting std.algorithm to ranges. You can't really pass an entire property to a function. This furthers the rift between properties and true fields. Consider: struct A { int x; } void foo(ref int n) { if (n == 0) n = 1; } ... A obj; foo(obj.x); So when passing obj.x to foo, foo can read and write it no problem. But if x is a property of A, it all falls apart: obj.x means just reading the property, not getting the property with all of its get and set splendor.
Jan 26 2009
Sean Kelly wrote:Don wrote:At least, it's something you could do in a lint tool. I think it would be a useful warning for classes. It still wouldn't solve Andrei's problem, though, because you still need public fields of structs to be POD fields, and a struct can still have properties. Then there are module properties... TheAndrei Alexandrescu wrote:I'm hesitant to assert that it should be illegal to take the address of a public data member, but this is certainly an intriguing idea.Jacob Carlborg wrote: [on properties]I'm starting to wonder if we need some restrictions on fields, in order to make properties and fields interchangable. The idea that a field could eventually be completely replaceable with functions is appealing, but I think it's only possible with a huge performance hit. One can distinguish between POD fields (for which any operation is legal) and property fields (which you cannot take the address of, for example). Arguably classes should normally not contain public POD fields, only public property fields. Perhaps this is a useful concept.What about this: class Host { property acquire release prop { T get(); } } And then the compiler will automatically created the "acquire" and "release" methods. There could also be what I would like to call "property shortcuts" (ruby calls this attributes) like this: class Host { get acquire release T prop; get set T prop2; }I was hoping I'd shield putative users from having to write verbose code. Besides, there's one other issue I stumbled upon while working on porting std.algorithm to ranges. You can't really pass an entire property to a function. This furthers the rift between properties and true fields. Consider: struct A { int x; } void foo(ref int n) { if (n == 0) n = 1; } ... A obj; foo(obj.x); So when passing obj.x to foo, foo can read and write it no problem. But if x is a property of A, it all falls apart: obj.x means just reading the property, not getting the property with all of its get and set splendor.only other option I can think of would be to make swap a built-in property of concrete types and expecting the user to call a.swap(b), though it seems weird that a.swap(b) couldn't be rewritten as swap(a,b) for any concrete type, regardless of context.Sean
Jan 27 2009
Jacob Carlborg wrote:Andrei Alexandrescu wrote:I agree with Daniel and I think that 99.999% of users will use properties with just get/set methods and those should be the methods for defining properties, _not_ acquire/release as above. the other 0.001% can just define extra functions in the body of the property. The design should be geared towards the common and simple case.Michiel Helvensteijn wrote:What about this: class Host { property acquire release prop { T get(); } } And then the compiler will automatically created the "acquire" and "release" methods. There could also be what I would like to call "property shortcuts" (ruby calls this attributes) like this: class Host { get acquire release T prop; get set T prop2; }Andrei Alexandrescu wrote:But these are too many. These should suffice: class Host { property prop { T get(); void acquire(ref T value) { ... } void release(ref T value) { ... } } } set() can be implemented as acquire from a copy, and swap can be implemented by calls to acquire and release. Technically, get() could be also implemented with acquire and release, but that's too intensive for most types. The problem I'm seeing is that most people won't want to go through the trouble of implementing three functions for one property. Andreivoid move(ref T src, ref T dst); void swap(ref T lhs, ref T rhs); ... But if "prop" is a property with get and set, everything falls apart. Properties effectively hide the address of the actual data and only traffic in values. That's often good (and sometimes even the only possibility in the case of properties computed on-the-fly), but this abstraction effectively makes resource-conserving move and swap impossible. ... What to do? I'd like to refine the notion of property such that moving properties around is possible, without, however, complicating their interface too much.I posted a possible solution to this (I think) in the last properties thread. I was thinking that properties might be equiped with extra member functions. One advantage is that you might get more efficiency. I think you just named another. class Host { property prop { T get() { ... } void set(T value) { ... } void move(ref T dst) { ... } void swap(ref T rhs) { ... } } }
Jan 26 2009
"Andrei Alexandrescu" wroteI realized that properties are not a complete model for actual data. The current conventional wisdom about properties goes like this (T is some type): class Host { property prop { T get() { ... } void set(T value) { ... } } } If T is e.g. an int, it all works nicely. Now consider T is a highly-structured piece of data that holds resources. In that case we want to make sure T is not copied unwittingly such that the resource is managed properly. This means that T has a copy constructor and a destructor. For such cases, extensive experience with C++ has shown that two primitives are essential: move and swap. void move(ref T src, ref T dst); void swap(ref T lhs, ref T rhs); Move takes the guts of src, puts them in dst, and then clears src effectively relieving it from any resource. Swap exchanges the guts of src and dst without any extra resource copying. Now if "prop" were a classic data, swapping host1.prop and host2.prop is a piece of cake (that is realized inside std.algorithm. Look it up, the implementation has quite a few interesting quirks!) But if "prop" is a property with get and set, everything falls apart. Properties effectively hide the address of the actual data and only traffic in values. That's often good (and sometimes even the only possibility in the case of properties computed on-the-fly), but this abstraction effectively makes resource-conserving move and swap impossible. I noticed this problem when dealing with ranges. The .head() function, if it returns a value, cannot be swapped/moved. Same applies to opIndex when implemented as a value property (via the opIndex/opIndexAssign tandem). What to do? I'd like to refine the notion of property such that moving properties around is possible, without, however, complicating their interface too much.There are two possibilities that I can think of. 1. ref return from get, which gets you part-way there. You can still do some useful things like which member you are returning getting calculated, but it doesn't make the call to set when you assign to the property, or allow calling get multiple times to return different values. 2. Have a "property delegate", which is like a normal delegate, but contains 2 functions. Calling a function which takes such a delegate makes the compiler generate such a delegate in the case where a simple lvalue is passed. You could signify it with the terms "lazy ref" in the function signature. option 1 is the simple solution, and covers some ground. I don't think it's universally useful, but it could provide a mechanism for things like head() to work. option 2 solves the problem, but introduces some complex, possibly performance hurting mechanisms. The way to solve this last part is probably to allow specification of multiple functions, and have the compiler choose the non 'lazy-ref' version if it is passed a simple lvalue. class C { private int _x; private int _y; this(int n) { _x = n; } property prop { int get() { return _y; } void set(int x) { _y += _x * x; } // some weird semantics for set } } void foo(lazy ref z) { z += 100; } translates to: struct property_delegate(T) { void *ptr; T function(void*) get; void function(void *, T) set; } void foo(property_delegate!(int) pd) { pd.set(pd.ptr, pd.get(pd.ptr) + 100); } usage: void bar() { auto c = new C(2); /* generates a property delegate with prop.get and prop.set as the functions, and c as the pointer */ foo(c.prop); assert(c.prop == 200); int n = c.prop; /* generates a property delegate with get and set functions being generated inner functions of bar which set and get n, and the stack frame as the pointer. */ foo(n); assert(n == 300); } -Steve
Jan 26 2009
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteYah, exactly. One thing I'm rather sad about is that certain containers won't be implementable that way, for example sparse vectors (with most elements zero). A sparse vector would want to return by value from head() and opIndex() (so it can return 0.0 most of the time) and would also want to have control over setting slots. So in brief, sparse vectors won't be supported naturally by our paradigm. They will still be supported, just not in a manner that makes them next to indistinguishable from dense vectors. Lately I'm getting to think (sour grapes syndrome?) that probably that's a good thing. A sparse vector is very long - e.g. millions of items, and only has a few or a few hundred that are nonzero. It would be wasteful to ever iterate over the whole one million items; I'd rather use a custom algorithm that only "sees" the nonzero elements. It would be nice to have opApply work, but I guess I can live with that too.I realized that properties are not a complete model for actual data. The current conventional wisdom about properties goes like this (T is some type): class Host { property prop { T get() { ... } void set(T value) { ... } } } If T is e.g. an int, it all works nicely. Now consider T is a highly-structured piece of data that holds resources. In that case we want to make sure T is not copied unwittingly such that the resource is managed properly. This means that T has a copy constructor and a destructor. For such cases, extensive experience with C++ has shown that two primitives are essential: move and swap. void move(ref T src, ref T dst); void swap(ref T lhs, ref T rhs); Move takes the guts of src, puts them in dst, and then clears src effectively relieving it from any resource. Swap exchanges the guts of src and dst without any extra resource copying. Now if "prop" were a classic data, swapping host1.prop and host2.prop is a piece of cake (that is realized inside std.algorithm. Look it up, the implementation has quite a few interesting quirks!) But if "prop" is a property with get and set, everything falls apart. Properties effectively hide the address of the actual data and only traffic in values. That's often good (and sometimes even the only possibility in the case of properties computed on-the-fly), but this abstraction effectively makes resource-conserving move and swap impossible. I noticed this problem when dealing with ranges. The .head() function, if it returns a value, cannot be swapped/moved. Same applies to opIndex when implemented as a value property (via the opIndex/opIndexAssign tandem). What to do? I'd like to refine the notion of property such that moving properties around is possible, without, however, complicating their interface too much.There are two possibilities that I can think of. 1. ref return from get, which gets you part-way there. You can still do some useful things like which member you are returning getting calculated, but it doesn't make the call to set when you assign to the property, or allow calling get multiple times to return different values.2. Have a "property delegate", which is like a normal delegate, but contains 2 functions. Calling a function which takes such a delegate makes the compiler generate such a delegate in the case where a simple lvalue is passed. You could signify it with the terms "lazy ref" in the function signature.Yah, crossed my mind too. With the third function in tow (the one that does the resource acquisition or whatnot) things are getting rather overweight and it starts looking like a full-fledged local class with a vtable and the works might be an option. Local classes have access to the frame pointer of their parent, so probably things can be arranged to work. But the cost of the whole operation becomes too high. Andrei
Jan 26 2009
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote:A good point. Let's not forget that the main purpose of ranges being special is to support foreach. Once you start using ranges in different ways, then it becomes more of a generalized structure, up to the library designer as to how to solve these problems.There are two possibilities that I can think of. 1. ref return from get, which gets you part-way there. You can still do some useful things like which member you are returning getting calculated, but it doesn't make the call to set when you assign to the property, or allow calling get multiple times to return different values.Yah, exactly. One thing I'm rather sad about is that certain containers won't be implementable that way, for example sparse vectors (with most elements zero). A sparse vector would want to return by value from head() and opIndex() (so it can return 0.0 most of the time) and would also want to have control over setting slots. So in brief, sparse vectors won't be supported naturally by our paradigm. They will still be supported, just not in a manner that makes them next to indistinguishable from dense vectors. Lately I'm getting to think (sour grapes syndrome?) that probably that's a good thing. A sparse vector is very long - e.g. millions of items, and only has a few or a few hundred that are nonzero. It would be wasteful to ever iterate over the whole one million items; I'd rather use a custom algorithm that only "sees" the nonzero elements. It would be nice to have opApply work, but I guess I can live with that too.I guess I'm not really sure what the "acquire" method does. I saw you mention it in another post, but with no explanation as to what it actually does, just that it was needed. I'm sure I'm not getting what should be obvious, but if you could enlighten, I would appreciate it :) Having the two functions seems like a reasonable baggage compromise, if you want to control the get and set methods of a property and pass that control to an underlying function, you need at least that. If we start generalizing it to anything, then it starts looking like struct interfaces would be more suitable. For example, you could allow setting an int property using both an int and a string, do you want to pass both setters to a function? I think limiting it to just a set and a get function should be sufficient. -Steve2. Have a "property delegate", which is like a normal delegate, but contains 2 functions. Calling a function which takes such a delegate makes the compiler generate such a delegate in the case where a simple lvalue is passed. You could signify it with the terms "lazy ref" in the function signature.Yah, crossed my mind too. With the third function in tow (the one that does the resource acquisition or whatnot) things are getting rather overweight and it starts looking like a full-fledged local class with a vtable and the works might be an option. Local classes have access to the frame pointer of their parent, so probably things can be arranged to work. But the cost of the whole operation becomes too high.
Jan 26 2009
Steven Schveighoffer wrote:I guess I'm not really sure what the "acquire" method does. I saw you mention it in another post, but with no explanation as to what it actually does, just that it was needed. I'm sure I'm not getting what should be obvious, but if you could enlighten, I would appreciate it :)Say you have an object obj that has a Matrix property and you have a Matrix object handy. The Matrix is rather resource intensive so you'd rather not copy it unwittingly. Conventionally, if you say: Matrix m; ... fill matrix ... obj.matrix = m; then m is copied into obj.matrix (there is the by-value call to set property). Now it's possible to arrange things such that m is destructively copied, but as the auto_ptr disaster has shown, it's not that intuitive to make assignment destroy the right hand side. So we'd need a distinct method: obj.matrix.acquire(m); That method takes the matrix by reference, sucks its life out of it, and leaves an empty shell behind. Pretty much like in your average horror movie.Having the two functions seems like a reasonable baggage compromise, if you want to control the get and set methods of a property and pass that control to an underlying function, you need at least that. If we start generalizing it to anything, then it starts looking like struct interfaces would be more suitable. For example, you could allow setting an int property using both an int and a string, do you want to pass both setters to a function? I think limiting it to just a set and a get function should be sufficient.With only get and set I can't implement a nothrow swap, which kinda bends me out of shape. Andrei
Jan 26 2009
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote:So what you are saying is that you want to control the set such that it destroys the source (essentially, I'm reading, you don't want to do a deep copy, just a reference copy, and then null the original reference). Let me ask another question then... does a property have reason to have both set AND acquire? More specifically, does a function that takes a property as an argument as we have been discussing need both set and acquire passed to it? If not, you can simply pass the set method that you want, still only requiring a pair of functions. How to signal that to the compiler easily? Not so sure... but it does seem to me like a type should know how it should be copied, and likely doing it other ways should require special methods. I can think of possible library solutions to this, say a wrapper property struct that can call any two functions to do set or get.I guess I'm not really sure what the "acquire" method does. I saw you mention it in another post, but with no explanation as to what it actually does, just that it was needed. I'm sure I'm not getting what should be obvious, but if you could enlighten, I would appreciate it :)Say you have an object obj that has a Matrix property and you have a Matrix object handy. The Matrix is rather resource intensive so you'd rather not copy it unwittingly. Conventionally, if you say: Matrix m; ... fill matrix ... obj.matrix = m; then m is copied into obj.matrix (there is the by-value call to set property). Now it's possible to arrange things such that m is destructively copied, but as the auto_ptr disaster has shown, it's not that intuitive to make assignment destroy the right hand side. So we'd need a distinct method: obj.matrix.acquire(m); That method takes the matrix by reference, sucks its life out of it, and leaves an empty shell behind. Pretty much like in your average horror movie.Sorry, don't get that either. Why can't set and get be used in a nothrow swap? -SteveHaving the two functions seems like a reasonable baggage compromise, if you want to control the get and set methods of a property and pass that control to an underlying function, you need at least that. If we start generalizing it to anything, then it starts looking like struct interfaces would be more suitable. For example, you could allow setting an int property using both an int and a string, do you want to pass both setters to a function? I think limiting it to just a set and a get function should be sufficient.With only get and set I can't implement a nothrow swap, which kinda bends me out of shape.
Jan 26 2009
On Tue, 27 Jan 2009 01:52:26 +0300, Steven Schveighoffer <schveiguy yahoo.com> wrote:"Andrei Alexandrescu" wroteBecause set implies data copying (read: allocation) and copy constructor call, both of which can throw. How about the following - you call a swap with a dot syntax: int x = 42; int y = 13; x.swap(y); // calls global void swap(T)(ref T, ref T); or a specialized version, swap(ref int, ref int); User is, however, free to implement his own swap method: struct BigInt { ... void swap(ref BigInt other) { ... } ... } Or another way - "void swap(T)(ref T lhs, ref T rhs);" tries "lhs.swap(rhs);" first, and falls back to "auto tmp = lhs; lhs = rhs; rhs = tmp;" if no lhs.swap method is defined. This way you can define "void aquire(ref T newOwner, ref T data);" as "newOwner.aquire(data);" and fall back to "swap(newOwner, data); release(data);" on failure. "void release(ref T obj);" tries "obj.release();" first and falls back to "swap(obj, T());" or "obj = T();" on failure.Steven Schveighoffer wrote:So what you are saying is that you want to control the set such that it destroys the source (essentially, I'm reading, you don't want to do a deep copy, just a reference copy, and then null the original reference). Let me ask another question then... does a property have reason to have both set AND acquire? More specifically, does a function that takes a property as an argument as we have been discussing need both set and acquire passed to it? If not, you can simply pass the set method that you want, still only requiring a pair of functions. How to signal that to the compiler easily? Not so sure... but it does seem to me like a type should know how it should be copied, and likely doing it other ways should require special methods. I can think of possible library solutions to this, say a wrapper property struct that can call any two functions to do set or get.I guess I'm not really sure what the "acquire" method does. I saw you mention it in another post, but with no explanation as to what it actually does, just that it was needed. I'm sure I'm not getting what should be obvious, but if you could enlighten, I would appreciate it :)Say you have an object obj that has a Matrix property and you have a Matrix object handy. The Matrix is rather resource intensive so you'd rather not copy it unwittingly. Conventionally, if you say: Matrix m; ... fill matrix ... obj.matrix = m; then m is copied into obj.matrix (there is the by-value call to set property). Now it's possible to arrange things such that m is destructively copied, but as the auto_ptr disaster has shown, it's not that intuitive to make assignment destroy the right hand side. So we'd need a distinct method: obj.matrix.acquire(m); That method takes the matrix by reference, sucks its life out of it, and leaves an empty shell behind. Pretty much like in your average horror movie.Sorry, don't get that either. Why can't set and get be used in a nothrow swap? -SteveHaving the two functions seems like a reasonable baggage compromise, if you want to control the get and set methods of a property and pass that control to an underlying function, you need at least that. If we start generalizing it to anything, then it starts looking like struct interfaces would be more suitable. For example, you could allow setting an int property using both an int and a string, do you want to pass both setters to a function? I think limiting it to just a set and a get function should be sufficient.With only get and set I can't implement a nothrow swap, which kinda bends me out of shape.
Jan 26 2009
"Denis Koroskin" wroteOn Tue, 27 Jan 2009 01:52:26 +0300, Steven Schveighoffer <schveiguy yahoo.com> wrote:OK, I think I got what you and Andrei are saying. I had to think about this a while, and I think Denis is right. Leave the swap implementation up to the type, not the holder of the type. A property should be a simple interface to define how to access an object's data, which is the most common case. Putting in all these extra methods and features to allow the possibility of a non-specific swap function for certain uncommon cases seems like you are trying to remove complexity from an opaque low level function and adding the complexity to all properties in existance instead. i.e. if you want to sort an array of matrices which are expensive to make copies of, then you need a special swap, you can't use the generic one, because it's not a generic swap. Let the type tell you how to do it or let the developer pass in a swap function. Don't put the intelligence into the holder of the type(which in this case would be a range). For simple types, a complex swap or property interface isn't necessary. I hope that was clear, it's hard to express what I'm trying to say... -Steve"Andrei Alexandrescu" wroteBecause set implies data copying (read: allocation) and copy constructor call, both of which can throw. How about the following - you call a swap with a dot syntax: int x = 42; int y = 13; x.swap(y); // calls global void swap(T)(ref T, ref T); or a specialized version, swap(ref int, ref int); User is, however, free to implement his own swap method: struct BigInt { ... void swap(ref BigInt other) { ... } ... } Or another way - "void swap(T)(ref T lhs, ref T rhs);" tries "lhs.swap(rhs);" first, and falls back to "auto tmp = lhs; lhs = rhs; rhs = tmp;" if no lhs.swap method is defined. This way you can define "void aquire(ref T newOwner, ref T data);" as "newOwner.aquire(data);" and fall back to "swap(newOwner, data); release(data);" on failure. "void release(ref T obj);" tries "obj.release();" first and falls back to "swap(obj, T());" or "obj = T();" on failure.Steven Schveighoffer wrote:So what you are saying is that you want to control the set such that it destroys the source (essentially, I'm reading, you don't want to do a deep copy, just a reference copy, and then null the original reference). Let me ask another question then... does a property have reason to have both set AND acquire? More specifically, does a function that takes a property as an argument as we have been discussing need both set and acquire passed to it? If not, you can simply pass the set method that you want, still only requiring a pair of functions. How to signal that to the compiler easily? Not so sure... but it does seem to me like a type should know how it should be copied, and likely doing it other ways should require special methods. I can think of possible library solutions to this, say a wrapper property struct that can call any two functions to do set or get.I guess I'm not really sure what the "acquire" method does. I saw you mention it in another post, but with no explanation as to what it actually does, just that it was needed. I'm sure I'm not getting what should be obvious, but if you could enlighten, I would appreciate it :)Say you have an object obj that has a Matrix property and you have a Matrix object handy. The Matrix is rather resource intensive so you'd rather not copy it unwittingly. Conventionally, if you say: Matrix m; ... fill matrix ... obj.matrix = m; then m is copied into obj.matrix (there is the by-value call to set property). Now it's possible to arrange things such that m is destructively copied, but as the auto_ptr disaster has shown, it's not that intuitive to make assignment destroy the right hand side. So we'd need a distinct method: obj.matrix.acquire(m); That method takes the matrix by reference, sucks its life out of it, and leaves an empty shell behind. Pretty much like in your average horror movie.Sorry, don't get that either. Why can't set and get be used in a nothrow swap? -SteveHaving the two functions seems like a reasonable baggage compromise, if you want to control the get and set methods of a property and pass that control to an underlying function, you need at least that. If we start generalizing it to anything, then it starts looking like struct interfaces would be more suitable. For example, you could allow setting an int property using both an int and a string, do you want to pass both setters to a function? I think limiting it to just a set and a get function should be sufficient.With only get and set I can't implement a nothrow swap, which kinda bends me out of shape.
Jan 26 2009