digitalmars.D - Partial modification of DIP23 to allow module level property
- Kenji Hara (75/84) Feb 08 2013 I had thought about DIP23 for a while, to allow module-level
- Jonathan M Davis (20/22) Feb 08 2013 And why is it that the global function gets to be a setter property and ...
- kenji hara (29/54) Feb 08 2013 To get a balance between rule simplicity and actually possible use cases...
- kenji hara (9/30) Feb 08 2013 Ah... do you consider this case?
- Jonathan M Davis (23/59) Feb 08 2013 That and the fact that if front isn't actually a property, then you can ...
- kenji hara (3/73) Feb 08 2013 It makes sense... Thanks.
- Robert (5/8) Feb 09 2013 Except you ebrace the idea that front is a function (property or not)
- Jonathan M Davis (4/14) Feb 09 2013 It fails miserably when the element type of a range is callable. That's ...
- Robert (6/9) Feb 09 2013 It does not, you would have to do:
http://wiki.dlang.org/DIP23I had thought about DIP23 for a while, to allow module-level property function. As the conclusion, we can have that without breaking the essence in DIP23. ==========In a nutshell 2. A property may have EXACTLY ONE or EXACTLY TWO parameters, counting the implicit this parameter if at all. The ONE-parameter version is ALWAYS a getter, and the TWO-parameter version is ALWAYS a setter. There's no variadics, defaulted parameters, and such.We should change this sentence to: ---- 2. A property may have EXACTLY ZERO or EXACTLY ONE parameter, without counting the implicit this parameter if at all. The ZERO-parameter version is ALWAYS a getter, and the ONE-parameter version is ALWAYS a setter. There's no variadics, defaulted parameters, and such. ----Optional parens stay inNo modification is necessary.No module-level propertiesIt should be replaced to completely: ---- * Module level properties stay in There is still module-level property emulating a global variable. That means a property defined at module level must take either ZERO parameter (meaning its a getter) or ONE parameter (meaning it's a setter). // at module level private long _distance; property long distance() { return _distance; } property void distance(double d) { assert(d >= 0); _distance = cast(long)std.math.trunc(d); } unittest { distance = 3.1; assert(_distance == 3) auto d = distance; assert(d == 3); } To avoid syntactic ambiguity, it's forbidden to define property getter callable by using UFCS. property ref int front(int[] a) { return a[0]; } unittest { int[] arr = [1,2,3]; assert(front(arr) == 1); // OK assert(arr.front == 1); // compile-time error, "setter property function cannot call with UFCS" } Instead, such 'front' function would work if remove the property annotation. ref int front(int[] a) { return a[0]; } unittest { int[] arr = [1,2,3]; assert(front(arr) == 1); // OK assert(arr.front() == 1); // OK, UFCS assert(arr.front == 1); // Even OK, UFCS + optional paranthesis feature arr.front() = 2; // assign to returned ref assert(arr[0] == 2); arr.front = 3; // Even OK, optional parenthesis assert(arr[0] == 2); int* p = &arr.front; // Even OK, arr.front is rewritten to // front(arr), so '&' operator will be applied to the ref value returned from 'front'. } For the combination of UFCS and setter syntax, a function which takes TWO parameters and defined at module level can SPECIALLY be annotated with property. property void all(double[] x, int y) { x[] = cast(double) y; } unittest { auto d = [ 1.2, 3.4 ]; d.all = 42; // UFCS + setter syntax // rewritten to: all(d, 42) assert(d == [ 42.0, 42.0 ]); } ---- Regards. Kenji Hara
Feb 08 2013
On Saturday, February 09, 2013 06:15:34 Kenji Hara wrote:To avoid syntactic ambiguity, it's forbidden to define property getter callable by using UFCS.And why is it that the global function gets to be a setter property and the UFCS function doesn't? What's the advantage in choosing the UFCS function as the one that has to be a normal function which just happens to be called without parens (but could be called with them) over choosing the global getter property to be the one that's a function that can be called without parens? I'd much rather see the global getter have to be declared without property (and rely an the ability to be called without parens but be unable to enforce it). And I _definitely_ wouldn't want to set a precedence for front to be declared as a non-property function. It needs to be consistently called without parens to work in generic code, and if arrays are able to use front with parens, then we definitely risk a lot of range-based functions being written incorrectly due to the fact that far too often, range-based functions only end up being tested with arrays, and in that case, the parens wouldn't cause an error like they should. It wouldn't be caught until someone actually tried it with another type of range (possibly much later). Granted, the writer of the range-based function should have tested it better, but given that arrays are by far the most common type of range, I think that it's just asking for trouble to allow their front or back to be used with parens. - Jonathan M Davis
Feb 08 2013
2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>And why is it that the global function gets to be a setter property and the UFCS function doesn't? What's the advantage in choosing the UFCS function as the one that has to be a normal function which just happens to be called without parens (but could be called with them) over choosing the global getter property to be the one that's a function that can be called without parens?To get a balance between rule simplicity and actually possible use cases. In my long thought, I have found that property annotation is not really necessary for UFCS getters (e.g. std.array.front). After the implementation of "optional paranthesis" feature which described in DIP23, a use of UFCS getter will work as same as now - using left hand side of assignment (e.g. arr.front = value) and even getting address of returned ref (auto ptr = &arr.front) . Then, we can use a property function defined in module level and has exactly one parameter as a module level setter without ambiguity. This is much reasonable benefit compared to the cost.I'd much rather see the global getter have to be declared without property (and rely an the ability to be called without parens but be unable to enforce it).I think it will introduce an inconsistency against method property functions, as follows: - Method property should have zero parameter for getter, or one parameter for setter. Both should be annotated with property. - Module level property should have zero parameter for getter, but MUST not be annotated with property. Module level property should have one parameter for setter and should be annotated with property. This is much complicated than my proposed sentence: 2. A property may have EXACTLY ZERO or EXACTLY ONE parameter, without counting the implicit this parameter if at all. The ZERO-parameter version is ALWAYS a getter, and the ONE-parameter version is ALWAYS a setter. There's no variadics, defaulted parameters, and such. And I _definitely_ wouldn't want to set a precedence for front to bedeclared as a non-property function. It needs to be consistently called without parens to work in generic code, and if arrays are able to use front with parens, then we definitely risk a lot of range-based functions being written incorrectly due to the fact that far too often, range-based functions only end up being tested with arrays, and in that case, the parens wouldn't cause an error like they should. It wouldn't be caught until someone actually tried it with another type of range (possibly much later). Granted, the writer of the range-based function should have tested it better, but given that arrays are by far the most common type of range, I think that it's just asking for trouble to allow their front or back to be used with parens.Sorry I cannot imagine an actual use case you are considering. Kenji Hara
Feb 08 2013
2013/2/9 kenji hara <k.hara.pg gmail.com>2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>Ah... do you consider this case? import std.array : front; int delegate()[] eventHandlers; auto result = eventHandlers.front(); // want to _call_ eventHandlers[0] Indeed, under the my proposal, typeof(result) becomes `int delegate()`, not `int`. Hmmmm..... Kenji HaraAnd I _definitely_ wouldn't want to set a precedence for front to bedeclared as a non-property function. It needs to be consistently calledwithout parens to work in generic code, and if arrays are able to use front with parens, then we definitely risk a lot of range-based functions being written incorrectly due to the fact that far too often, range-based functions only end up being tested with arrays, and in that case, the parens wouldn't cause an error like they should. It wouldn't be caught until someone actually tried it with another type of range (possibly much later). Granted, the writer of the range-based function should have tested it better, but given that arrays are by far the most common type of range, I think that it's just asking for trouble to allow their front or back to be used with parens.Sorry I cannot imagine an actual use case you are considering.
Feb 08 2013
On Saturday, February 09, 2013 15:26:10 kenji hara wrote:2013/2/9 kenji hara <k.hara.pg gmail.com>That and the fact that if front isn't actually a property, then you can do stuff like this: auto func(R)(R range) if(isForwardRange!R) { auto f = range.front(); //... return blah; } That code would then happily compile with arrays but fail with every other type of range. But since arrays are the most common type of range, it seems fairly common for people to test range-based functions with just arrays, and so it becomes very easy for people write code which is supposed to be range- based but only works with arrays. But the delegate example does make it much worse, because then range-based functions wouldn't be able to rely on front() calling the callable returned by front in the cases where front returns a callable, since that would only work with ranges which weren't arrays. Optional parens in generic code using a generic API is just begging for trouble. Any property in such an API needs to actually be property so that it can't be called with parens and so that if it _is_ called with parens, it's attempted to use the parens on the return value. - Jonathan M Davis2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>Ah... do you consider this case? import std.array : front; int delegate()[] eventHandlers; auto result = eventHandlers.front(); // want to _call_ eventHandlers[0] Indeed, under the my proposal, typeof(result) becomes `int delegate()`, not `int`. Hmmmm.....And I _definitely_ wouldn't want to set a precedence for front to bedeclared as a non-property function. It needs to be consistently calledwithout parens to work in generic code, and if arrays are able to use front with parens, then we definitely risk a lot of range-based functions being written incorrectly due to the fact that far too often, range-based functions only end up being tested with arrays, and in that case, the parens wouldn't cause an error like they should. It wouldn't be caught until someone actually tried it with another type of range (possibly much later). Granted, the writer of the range-based function should have tested it better, but given that arrays are by far the most common type of range, I think that it's just asking for trouble to allow their front or back to be used with parens.Sorry I cannot imagine an actual use case you are considering.
Feb 08 2013
2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>On Saturday, February 09, 2013 15:26:10 kenji hara wrote:It makes sense... Thanks. Kenji Hara2013/2/9 kenji hara <k.hara.pg gmail.com>being2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>And I _definitely_ wouldn't want to set a precedence for front to bedeclared as a non-property function. It needs to be consistently calledwithout parens to work in generic code, and if arrays are able to use front with parens, then we definitely risk a lot of range-based functionsthewritten incorrectly due to the fact that far too often, range-based functions only end up being tested with arrays, and in that case, the parens wouldn't cause an error like they should. It wouldn't be caught until someone actually tried it with another type of range (possibly much later). Granted,thatwriter of the range-based function should have tested it better, but givenjustarrays are by far the most common type of range, I think that it'snotAh... do you consider this case? import std.array : front; int delegate()[] eventHandlers; auto result = eventHandlers.front(); // want to _call_ eventHandlers[0] Indeed, under the my proposal, typeof(result) becomes `int delegate()`,asking for trouble to allow their front or back to be used with parens.Sorry I cannot imagine an actual use case you are considering.`int`. Hmmmm.....That and the fact that if front isn't actually a property, then you can do stuff like this: auto func(R)(R range) if(isForwardRange!R) { auto f = range.front(); //... return blah; } That code would then happily compile with arrays but fail with every other type of range. But since arrays are the most common type of range, it seems fairly common for people to test range-based functions with just arrays, and so it becomes very easy for people write code which is supposed to be range- based but only works with arrays. But the delegate example does make it much worse, because then range-based functions wouldn't be able to rely on front() calling the callable returned by front in the cases where front returns a callable, since that would only work with ranges which weren't arrays. Optional parens in generic code using a generic API is just begging for trouble. Any property in such an API needs to actually be property so that it can't be called with parens and so that if it _is_ called with parens, it's attempted to use the parens on the return value.
Feb 08 2013
arrays are by far the most common type of range, I think that it's just asking for trouble to allow their front or back to be used with parens.Except you ebrace the idea that front is a function (property or not) and make parens work in all cases. Why not do it the other way round, front has to be a function property or not. Seems to work far better, it is a simple rule. No performance penalty as trivial set/get can easily be lined in, ...
Feb 09 2013
On Saturday, February 09, 2013 13:01:58 Robert wrote:It fails miserably when the element type of a range is callable. That's the main reason that property was introduced in the first place. - Jonathan M Davisarrays are by far the most common type of range, I think that it's just asking for trouble to allow their front or back to be used with parens.Except you ebrace the idea that front is a function (property or not) and make parens work in all cases. Why not do it the other way round, front has to be a function property or not. Seems to work far better, it is a simple rule. No performance penalty as trivial set/get can easily be lined in, ...
Feb 09 2013
On Sat, 2013-02-09 at 04:12 -0800, Jonathan M Davis wrote:It fails miserably when the element type of a range is callable. That's the main reason that property was introduced in the first place.It does not, you would have to do: front()(); Always! front is guaranteed to be a function, (I tried to explain why in the DIP), so this is consistent behaviour, meaning it will work with templates.
Feb 09 2013