January 19, 2012
On 19/01/12 12:51 AM, Timon Gehr wrote:
> On 01/19/2012 01:41 AM, Peter Alexander wrote:
>> On 18/01/12 12:52 AM, Timon Gehr wrote:
>>> On 01/18/2012 01:40 AM, Jonathan M Davis wrote:
>>>> On Tuesday, January 17, 2012 19:31:25 bearophile wrote:
>>>>> Nick Sabalausky:
>>>>>> Without properties, member function access *ANY* many value
>>>>>> accesses are "a.b()". Is this member value a plain-old-var or a
>>>>>> function?
>>>>>> Who knows! It's a leeked out implementation detail, hooray!
>>>>>
>>>>> I have a partially related question.
>>>>>
>>>>> Currently this code compiles even with -property:
>>>>>
>>>>> void main() {
>>>>> int[int] aa = [1:2];
>>>>> auto byval = aa.byValue();
>>>>> }
>>>>>
>>>>> But I think byValue is a property, so isn't it right to give a
>>>>> compilation
>>>>> error if you add () after the name of a property?
>>>>
>>>> Definitely a bug. Strict enforcement requires that parens be used on
>>>> all
>>>> function calls and that no properties use parens. If you use parens on
>>>> them,
>>>> that would mean that you're using them on the return value of the
>>>> property
>>>> (e.g. opCall) - and in fact, that's one of the main reasons that
>>>> @property was
>>>> added in the first place, since without enforcement, property
>>>> functions which
>>>> return a delegate result in an ambiguity.
>>>>
>>>> - Jonathan M Davis
>>>
>>> A related and way more embarrassing problem is that lazy function
>>> parameters have the same issue.
>>>
>>> This program prints nothing:
>>> import std.stdio;
>>> void foo(lazy void delegate() dg){
>>> dg();
>>> }
>>> void main(){
>>> foo({writeln("hello");});
>>> }
>>
>> Perhaps I'm wrong, but this issue is different.
>>
>> The code you have written is something that would be written by someone
>> that doesn't understand how lazy works.
>
> It is written by someone who understands how it should work.
>
>> You have to use () to un-lazy
>
> What would 'un-lazying' be and what is supposed to be its effect?

Under the hood, lazy is just a delegate with different syntax.

void foo(lazy int x) {}
int a;
foo(a++);

Essentially translates to:

void foo(int delegate() x) {}
int a;
foo( { return a++; } );

Inside foo, you have to do x() to evaluate the delegate, just like normals delegates. x on its own without parens would just be the delegate variable. If you had a lazy delegate parameter then that is like a delegate that returns a delegate, so you have to do x()() to evaluate the returned delegate.

>> it, and then () again to invoke the delegate. There is no ambiguity like
>> there is with property delegates.
>
> The code has exactly the same semantics whether or not you write the
> parens after a lazy variable except for the case when it is a delegate
> or function pointer.

I was not aware of this. I believe this is a bug (or at least a design flaw).
January 19, 2012
On 1/17/12 8:29 PM, Andrei Alexandrescu wrote:
> On 1/17/12 5:13 PM, Peter Alexander wrote:
>> On 17/01/12 10:11 PM, Jonathan M Davis wrote:
>>> You would need to come up with some really solid arguments why it
>>> should be
>>> thrown out (and what we should do instead) and get both Walter and
>>> Andrei (if
>>> not the community at large) to agree that they not only prefer your
>>> proposal
>>> but that it's worth the issues that the changes are going to cause at
>>> this
>>> stage.
>>
>> There's a few good reasons to throw it out:
>>
>> 1. Avoids pointless discussions like this one. These discussions add
>> nothing, it's just mindless bike shedding.
>
> Yes.
>
>> 2. The -property flag *creates* a new kind of error, but doesn't
>> actually help find real problems with your code. Without properties,
>> member function access would always be a.b(), and this artificial error
>> could be avoided.
>
> Yes! I can't believe we have a check that has _zero_ contribution to
> improving code.
>
>> 3. Properties introduce another thing to remember, with no value ("was
>> it byKeys, or byKeys()?"). Without properties, it would be byKeys(). No
>> need to remember.
>
> YES!!!
>
>> 4. Properties obfuscate code. Is (a.b = c) a variable assignment or
>> arbitrary function call? Who knows! Is a.b an actual variable? Can I
>> write &a.b to get its address, or is it a function masquerading as a
>> variable?
>
> Hm, actually that's not bad.
>
>> 5. One less language feature to implement, learn, document, debug, and
>> discuss.
>
> Back to yes.
>
>> Is it practical or realistic to throw it out at this stage? I don't
>> know. But there are reasons to.
>
> Me neither. If I had my way I'd carefully redo the feature to only
> require @property on rare cases that would otherwise be ambiguous, and
> make parens optional everywhere else.
>
>
> Andrei

Before I knew Ruby I was in favor of @property. Now I'm against it.
January 19, 2012
On 17.01.2012 07:48, Andrei Alexandrescu wrote:
> I hate I must ask this:
>
> int[string] aa;
> foreach (k; aa.byKey) { ... }
>
> or
>
> int[string] aa;
> foreach (k; aa.byKey()) { ... }
>

For it to be a property, I think you should be able to simplify this example:

---
auto k = aa.byKey;
writeln(k.front);
k.popFront();
writeln(k.front);
---

to this:

---
writeln(k.byKey.front);
k.byKey.popFront();
writeln(k.byKey.front);
---

and get the same result.  But my understanding is that you wouldn't, in which case byKey doesn't sense to me as a property.  It creates and returns a new range object each time you call it, right?
January 19, 2012
On 19.01.2012 18:19, torhu wrote:
> On 17.01.2012 07:48, Andrei Alexandrescu wrote:
>>  I hate I must ask this:
>>
>>  int[string] aa;
>>  foreach (k; aa.byKey) { ... }
>>
>>  or
>>
>>  int[string] aa;
>>  foreach (k; aa.byKey()) { ... }
>>
>
> For it to be a property, I think you should be able to simplify this
> example:
>
> ---
> auto k = aa.byKey;
> writeln(k.front);
> k.popFront();
> writeln(k.front);
> ---
>
> to this:
>
> ---
> writeln(k.byKey.front);
> k.byKey.popFront();
> writeln(k.byKey.front);
> ---
>
> and get the same result.  But my understanding is that you wouldn't, in
> which case byKey doesn't sense to me as a property.  It creates and
> returns a new range object each time you call it, right?

Sorry, I meant this for the second example:

---
writeln(aa.byKey.front);
aa.byKey.popFront();
writeln(aa.byKey.front);
---
January 19, 2012
On 1/19/12 11:19 AM, torhu wrote:
> On 17.01.2012 07:48, Andrei Alexandrescu wrote:
>> I hate I must ask this:
>>
>> int[string] aa;
>> foreach (k; aa.byKey) { ... }
>>
>> or
>>
>> int[string] aa;
>> foreach (k; aa.byKey()) { ... }
>>
>
> For it to be a property, I think you should be able to simplify this
> example:
>
> ---
> auto k = aa.byKey;
> writeln(k.front);
> k.popFront();
> writeln(k.front);
> ---
>
> to this:
>
> ---
> writeln(k.byKey.front);
> k.byKey.popFront();
> writeln(k.byKey.front);
> ---
>
> and get the same result. But my understanding is that you wouldn't, in
> which case byKey doesn't sense to me as a property. It creates and
> returns a new range object each time you call it, right?

Yah, this is the lvalue vs. rvalue part. I think you are making a good argument.

Andrei
January 19, 2012
On 19.01.2012 18:21, torhu wrote:
> On 19.01.2012 18:19, torhu wrote:
>>  On 17.01.2012 07:48, Andrei Alexandrescu wrote:
>>>   I hate I must ask this:
>>>
>>>   int[string] aa;
>>>   foreach (k; aa.byKey) { ... }
>>>
>>>   or
>>>
>>>   int[string] aa;
>>>   foreach (k; aa.byKey()) { ... }
>>>
>>
>>  For it to be a property, I think you should be able to simplify this
>>  example:
>>
>>  ---
>>  auto k = aa.byKey;
>>  writeln(k.front);
>>  k.popFront();
>>  writeln(k.front);
>>  ---
>>
>>  to this:
>>
>>  ---
>>  writeln(k.byKey.front);
>>  k.byKey.popFront();
>>  writeln(k.byKey.front);
>>  ---
>>
>>  and get the same result.  But my understanding is that you wouldn't, in
>>  which case byKey doesn't sense to me as a property.  It creates and
>>  returns a new range object each time you call it, right?
>
> Sorry, I meant this for the second example:
>
> ---
> writeln(aa.byKey.front);
> aa.byKey.popFront();
> writeln(aa.byKey.front);
> ---

The reason would be that if it looks like field access, it should behave like that.  People will quickly learn how this works, like they have gotten used to .dup and similar, but what happens when third-party libraries start doing it too?  It think this is not a good precedence to set.  We already have fields and functions, this would be a third kind, one that looks like a field but behaves more like a function.
January 19, 2012
On 01/19/2012 03:47 PM, Peter Alexander wrote:
> On 19/01/12 12:51 AM, Timon Gehr wrote:
>> On 01/19/2012 01:41 AM, Peter Alexander wrote:
>>> On 18/01/12 12:52 AM, Timon Gehr wrote:
>>>> On 01/18/2012 01:40 AM, Jonathan M Davis wrote:
>>>>> On Tuesday, January 17, 2012 19:31:25 bearophile wrote:
>>>>>> Nick Sabalausky:
>>>>>>> Without properties, member function access *ANY* many value
>>>>>>> accesses are "a.b()". Is this member value a plain-old-var or a
>>>>>>> function?
>>>>>>> Who knows! It's a leeked out implementation detail, hooray!
>>>>>>
>>>>>> I have a partially related question.
>>>>>>
>>>>>> Currently this code compiles even with -property:
>>>>>>
>>>>>> void main() {
>>>>>> int[int] aa = [1:2];
>>>>>> auto byval = aa.byValue();
>>>>>> }
>>>>>>
>>>>>> But I think byValue is a property, so isn't it right to give a
>>>>>> compilation
>>>>>> error if you add () after the name of a property?
>>>>>
>>>>> Definitely a bug. Strict enforcement requires that parens be used on
>>>>> all
>>>>> function calls and that no properties use parens. If you use parens on
>>>>> them,
>>>>> that would mean that you're using them on the return value of the
>>>>> property
>>>>> (e.g. opCall) - and in fact, that's one of the main reasons that
>>>>> @property was
>>>>> added in the first place, since without enforcement, property
>>>>> functions which
>>>>> return a delegate result in an ambiguity.
>>>>>
>>>>> - Jonathan M Davis
>>>>
>>>> A related and way more embarrassing problem is that lazy function
>>>> parameters have the same issue.
>>>>
>>>> This program prints nothing:
>>>> import std.stdio;
>>>> void foo(lazy void delegate() dg){
>>>> dg();
>>>> }
>>>> void main(){
>>>> foo({writeln("hello");});
>>>> }
>>>
>>> Perhaps I'm wrong, but this issue is different.
>>>
>>> The code you have written is something that would be written by someone
>>> that doesn't understand how lazy works.
>>
>> It is written by someone who understands how it should work.
>>
>>> You have to use () to un-lazy
>>
>> What would 'un-lazying' be and what is supposed to be its effect?
>
> Under the hood, lazy is just a delegate with different syntax.
>

It is more than that, type checking works differently for lazy values compared to scoped delegates.

> void foo(lazy int x) {}
> int a;
> foo(a++);
>
> Essentially translates to:
>
> void foo(int delegate() x) {}
> int a;
> foo( { return a++; } );
>
> Inside foo, you have to do x() to evaluate the delegate, just like
> normals delegates. x on its own without parens would just be the
> delegate variable. If you had a lazy delegate parameter then that is
> like a delegate that returns a delegate, so you have to do x()() to
> evaluate the returned delegate.

I know what is under the hood, but I don't really care. Lazy is just another storage class, and as such it has no business changing the syntax using which the value is accessed.

>
>>> it, and then () again to invoke the delegate. There is no ambiguity like
>>> there is with property delegates.
>>
>> The code has exactly the same semantics whether or not you write the
>> parens after a lazy variable except for the case when it is a delegate
>> or function pointer.
>
> I was not aware of this. I believe this is a bug (or at least a design
> flaw).

Parens should be removed completely from lazy values. They don't add a lot while being inconsistent with how other STCs work.
January 19, 2012
On 1/19/12 11:29 AM, torhu wrote:
> The reason would be that if it looks like field access, it should behave
> like that.

The difficulty is in knowing where to stop. The only things that behaves exactly like a field access is a field access. This is a known issue in C++, e.g. smart pointers "are smart but aren't pointers".

Andrei
January 19, 2012
On Thu, 19 Jan 2012 12:19:09 -0500, torhu <no@spam.invalid> wrote:

> On 17.01.2012 07:48, Andrei Alexandrescu wrote:
>> I hate I must ask this:
>>
>> int[string] aa;
>> foreach (k; aa.byKey) { ... }
>>
>> or
>>
>> int[string] aa;
>> foreach (k; aa.byKey()) { ... }
>>
>
> For it to be a property, I think you should be able to simplify this example:
>
> ---
> auto k = aa.byKey;
> writeln(k.front);
> k.popFront();
> writeln(k.front);
> ---
>
> to this:
>
> ---
> writeln(k.byKey.front);
> k.byKey.popFront();
> writeln(k.byKey.front);
> ---
>
> and get the same result.  But my understanding is that you wouldn't, in which case byKey doesn't sense to me as a property.  It creates and returns a new range object each time you call it, right?

That's like saying this:

int[] arr;

int l = arr.length;
l++;

should be the same as this:

arr.length++;

because it's a property.

This is an orthogonal problem.  byKey doesn't try to affect the original AA, so the semantics should be the same whether you save a copy or access it in one line.

There are no hard-fast rules on what should be properties and what shouldn't.  But the rvalue vs lvalue is an orthogonal problem.

-Steve
January 19, 2012
On Thursday, January 19, 2012 18:33:15 Timon Gehr wrote:
> Parens should be removed completely from lazy values. They don't add a lot while being inconsistent with how other STCs work.

Ignoring the fact that this will break code, I completely agree. However, given the fact that it's going to break code, I question that you we could get Walter to agree. It would definitely lead to the language being more consistent though.

- Jonathan M Davis