On Fri, 08 Feb 2013 16:45:58 -0500, Robert <[email protected]> wrote:
First of all, thanks for this very insight full discussion. I start to
understand the other side. :-)
You are welcome! I also am learning things and shaping my opinions based
on these discussions.
On Fri, 2013-02-08 at 15:13 -0500, Steven Schveighoffer wrote:
On Fri, 08 Feb 2013 14:05:40 -0500, Robert <[email protected]> wrote:
> all it states that you can get the current element by issuing:
> r.front
> which would still be possible with the optional parentheses of DIP23.
Technically this is true. But the expectation is that r.front is a
property/field, not a function. That aspect is difficult to capture in
a
template, especially in the current implementation where @property on a
getter is essentially redundant info.
In a template you should not care what it actually is, as long as this
works:
auto h=r.front;
(stolen from the implementation of isForwardRange), if I understood your
argument correctly.
In the grand scheme of things, the fact that a range's front accessor is a
property or a function is not technically important. But front being a
property IS important for specific uses. You can't have it so that front
is a function only when using it as a range, and a property when using it
as a delegate getter.
In fact, the only case where a properly-implemented @property would be
required on a getter is for delegate properties. I would argue that if
@property worked correctly, it would be possible and correct to require
r.front to be a property in that template.
I don't understand, what is the exact problem with: r.front()()? Despite
not matching the specification regarding no parentheses for properties?
The problem is, isInputRange does not require that it is a function, but
you ARE requiring that by specifying arbitrary rules.
Imagine an infinite range (one which never ends), that returns exactly a
specific delegate. Such a range could be implemented:
struct infiniteDelegate
{
int delegate() front;
enum empty = false;
void popFront() {};
}
Such a range would fit the definition of input range, but unlike an array,
r.front() is how you call the delegate, not r.front()().
Making a consistent interface is not possible without @property.
Module level properties pose an issue, because @property does not
designate whether a property is a getter or a setter. In particular, a
single-arg property function could be considered both a module property
setter, or a UFCS property getter.
So your issue is not really that UFCS properties are wrong, it's that
disabling module-level properties is wrong. I agree with you, but at
the
same time, module level properties are not as common or useful as UFCS.
Sure UFCS for functions, I suggest that UFCS for properties does not
make a lot of sense. (If you apply my definition of a property and
accept simple functions with optional parentheses as better way.)
Actually, that is not true. Check out the definition of
hasAssignableElements:
http://dlang.org/phobos/std_range.html#.hasAssignableElements
By your requirements, hasAssignableElements!(T[]) would necessarily always
be false, since UFCS properties are banned, so array.front is not a
property. You are saying, I can't assign to the front element of an
array. That doesn't sit well with the rest of array's API.
I am sorry, I don't understand what you mean here with:
In fact, one could argue they are more confusing since module scope is
one
of the only scopes that can be obscured.
int x;
void foo()
{
int x; // allowed
x = 5; // applies to local scope, not module
{
int x; // error! shadows outer scope x.
}
}
So the point is, a global property could possibly be obscured if you
declared a local variable with the same name.
Note also that you can have struct-qualified properties (at least you
should be able to, it seems Andrei's DIP does not allow it, but it's not
at odds with UFCS). Those are just about equivalent to module-level
properties, they just require specifying the struct name (and people have
used this in clever ways to achieve good readable results).
There are other possible solutions, such as designating something as a
getter specifically, or adding more syntax (i.e. decorating the first
parameter as 'this' for a module-level UFCS getter). But for now, the
easiest thing is to disallow one or the other, and since UFCS properties
are pretty much essential in Phobos, he's disabling the module level
properties.
The 42.fun = 43 example, just about everything is wrong with that.
Look here is another function that D "allows":
void increment(int x);
What is that exactly supposed to do?
42.increment(); // what?
increment(42); // what?
Design and naming of functions is never going to be a problem that the
compiler can solve.
Point taken. But my point is, that with optional parentheses UFCS
properties are not needed, so the cleaner solution is to forbid UFCS
properties. (See my DIP for further reasoning when it is ready)
What about setters?
My point on that was, if something works but is not supposed to, we
shouldn't have to worry about keeping that code working. @property in
its
current form is NOT implemented as it was designed. There is no
requirement to keep existing behavior that doesn't correctly work.
In the example given, @property is supposed to ban the use of
parentheses
according to the spec, but it doesn't. In fact, in the case of a
@property that returns a delegate, it REQUIRES the extra parentheses.
This is a bug, and not something to try and keep working.
Despite being inconsistent with the specification, what are the problems
with that?
I'm not saying it's a problem, I suppose it could be considered by some,
desirable to require the extra parentheses. I haven't heard from that
person yet.
But "being backwards compatible" is not a good reason when you are talking
about incorrect behavior. It would have to be because that is a desirable
feature. Frankly, if that was the case, I think we would never have
introduced @property.
On the other hand, ref @properties is NOT a bug, it functions as
designed. Whether to remove it or not is certainly a discussion we can
have, but it's not buggy behavior. See the difference?
But the documentation of properties: http://dlang.org/property.html
in the section about user defined properties also states in the very
first sentence:
Properties are functions that can be syntactically treated as
if they were fields or variables.
which is clearly not true at the moment. If this sentence got removed
and replaced with a big fat warning that properties are not exchangeable
with fields, this would be the least that should be done.
It certainly is true. They can be treated like fields for getting and
setting, and I think "syntactically" means you access them like you would
access a field.
You might say "yeah, but you can't do ++ on them!", but you can't do that
to a const field either. Should const fields be considered fields?
A ref property pretty much behaves EXACTLY like a field.
The current thinking instead seems to be that properties are just
syntactic sugar and the level of encapsulation is business that is left
to the developer.
I don't think that SHOULD be D's business. Encapsulation should be
possible, encouraged, but not dictated.
My thinking on the other hand is, that properties are the method of
encapsulation, so they should ensure it. If something does not, it is
not a property, which leads to a very clean design of properties.
Such a design is entirely possible today, even if ref properties exist,
even if *properties* didn't exist. Why must you use ref properties just
because they are available?
Note that the reader/user of code is never guaranteed that they are
working with properly encapsulated code. A property looks like a field,
and a field is NOT encapsulated. So any compiler/language guarantees of
encapsulation with properties are moot.
-Steve