www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Partial modification of DIP23 to allow module level property

reply "Kenji Hara" <k.hara.pg gmail.com> writes:
 http://wiki.dlang.org/DIP23

I 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 in

No modification is necessary.
 No module-level properties

It 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
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
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
prev sibling next sibling parent kenji hara <k.hara.pg gmail.com> writes:
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 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.

Sorry I cannot imagine an actual use case you are considering. Kenji Hara
Feb 08 2013
prev sibling next sibling parent kenji hara <k.hara.pg gmail.com> writes:
2013/2/9 kenji hara <k.hara.pg gmail.com>

 2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>

 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.

Sorry I cannot imagine an actual use case you are considering.

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 Hara
Feb 08 2013
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 09, 2013 15:26:10 kenji hara wrote:
 2013/2/9 kenji hara <k.hara.pg gmail.com>
 
 2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>
 
 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.

Sorry I cannot imagine an actual use case you are considering.

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.....

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 Davis
Feb 08 2013
prev sibling next sibling parent kenji hara <k.hara.pg gmail.com> writes:
2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>

 On Saturday, February 09, 2013 15:26:10 kenji hara wrote:
 2013/2/9 kenji hara <k.hara.pg gmail.com>

 2013/2/9 Jonathan M Davis <jmdavisProg gmx.com>

 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.

Sorry I cannot imagine an actual use case you are considering.

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.....

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.

It makes sense... Thanks. Kenji Hara
Feb 08 2013
prev sibling next sibling parent Robert <jfanatiker gmx.at> writes:
 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
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, February 09, 2013 13:01:58 Robert wrote:
 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, ...

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 Davis
Feb 09 2013
prev sibling parent Robert <jfanatiker gmx.at> writes:
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