October 03, 2008
On 2008-10-03 06:27:24 -0400, Fawzi Mohamed <fmohamed@mac.com> said:

> So actually automatically evaluating the delegate would actually fix this particular problem (but would indeed introduce many other problems, like how to then get the delegate).

Hum... it could work a little like references (&) in C++. Basically, if you assign a lazy value to another lazy value, it just copy the delegate. Obviously, you'd need to allow "lazy" as a type modifier everywhere, in the same sense as "ref" could (will?) be made a type modifier acceptable everywhere.

void func(lazy int x)
{
	lazy int y = x; // assigns x delegate to y.
	y; // calls y delegate, which is the same as x.
	x; // calls x delegate.
}

We could even extend the concept to create "lazy values", which could work as getters for properties, and disallow parenthesis for them:

lazy int func { return 1; }
lazy int delegate() deleg { return &func; }

int i = func; // calls func.
int j = func(); // error, func is lazy and cannot be called with parenthesis.
int k = deleg(); // calls deleg(), then evaluate the returned delegate.

lazy int x = func; // assigns &func to x.
lazy int delegate() y = deleg; // assigns &deleg to y.
lazy int z = deleg(); // implicitly creates a delegate for calling deleg() lazily.

Doesn't this generalize well?

I'm just not sure how to extend this to a syntax for setters though. Ideas?

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

October 03, 2008
Sergey Gromov wrote:
> Thu, 02 Oct 2008 15:03:42 -0500,
> Andrei Alexandrescu wrote:
>> Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... any ideas? :o)
>>
>> One goal is to fix opIndexAssign and make it work similar to the way it works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test bed.
> 
> What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

Probably performance.

Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1.

But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c).

(The same idea can be extended to properties and .opSlice() )
October 03, 2008
Sergey Gromov wrote:
> Thu, 02 Oct 2008 15:03:42 -0500,
> Andrei Alexandrescu wrote:
>> Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... any ideas? :o)
>>
>> One goal is to fix opIndexAssign and make it work similar to the way it works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test bed.
> 
> What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?

One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.

Another problem (assuming the above is fixed) is that b will be looked up twice in the hash.


Andrei
October 03, 2008
Bruno Medeiros wrote:
> Andrei Alexandrescu wrote:
> Yes, we would need an alternate mechanism for properties - an addition to the language. And it's quite true that it's likely there would be disagreement in the "lot of people" about that. But there is only one way to be sure, so we could at least try! Would you and Walter at least consider this, and see if we could find an alternative that satisfies a fair number of people? Try without compromise.

What I'd consider is not that important. I do know what Walter's viewpoint is, and that is that there are plenty of bigger rocks to move.

> In fact, I'm also not a fan of those complex property mechanisms, à lá C#. I think a fair candidate would be Bill Baxter's proposal, the 'property' keyword:
> 
>   property int foo() { return _foo; };
>   property void foo(int foo) { _foo = foo; };
> 
> The property keyword would make a function callable *only* as a property syntax (either as reading, 'bar = foo;', or as writing, 'foo = 42;'). A function signature which was not adequate for property access would be compile-time error.
> This proposal fixes the ambiguities issues, and require *minimal changes* to the language, both in terms of syntax and semantics!

Then what would obj.fun mean when fun is not a property?

Andrei

October 03, 2008
Bruno Medeiros wrote:
> Hold it right there! Are you saying that the existence of 'lazy' somehow required, or helped justify the existence of the omittable parenthesis functionality? How is that?
> I think you are accusing innocents ('lazy') or wrongdoing. 'lazy' has nothing to do with the omittable parenthesis functionality, I think they are completely orthogonal. Prove me wrong otherwise.

The connection is indirect. Lazy showed that omitting trailing parens after delegate names is not recommended. If trailing parens after delegate names were required, much of the ambiguity mentioned by Sergey would disappear.

Andrei
October 03, 2008
Sergey Gromov wrote:
> Fri, 3 Oct 2008 09:37:46 +0900,
> Bill Baxter wrote:
>> On Fri, Oct 3, 2008 at 9:32 AM, Bill Baxter <wbaxter@gmail.com> wrote:
>>> On Fri, Oct 3, 2008 at 8:04 AM, Sergey Gromov <snake.scaly@gmail.com> wrote:
>>>> Thu, 02 Oct 2008 15:03:42 -0500,
>>>> Andrei Alexandrescu wrote:
>>>>> Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask...
>>>>> any ideas? :o)
>>>>>
>>>>> One goal is to fix opIndexAssign and make it work similar to the way it
>>>>> works in arrays, e.g. a[b] += c. Indexing into hash tables is a good
>>>>> test bed.
>>>> What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?
>>>>
>>> Indeed.  I thought there wasn't a lot of debate needed on this, just action.
>> .
>> ... except these extras do have the same issue that plain property
>> assignment does.  They would open up a new class of things that are
>> valid code but don't behave as expected.  writefln += 5.
>>
>> And also, a[b] += c should probably be rewritten as  "a.opIndex(b) +=
>> c"  *if* a returns a reference of some sort.  Ok, so maybe there is a
>> little to talk about.  :-)
> 
> I think that any expression "a @= b" should first check whether the expression on the left is an lvalue.  If yes then use it as any other lvalue, otherwise try to re-write an expression using op@Assign.  This works in many scenarios, like opIndex returning a reference.

That's a good rule, but a[b] @= c on a hash is not helped by it.

Andrei
October 03, 2008
KennyTM~ wrote:
> Sergey Gromov wrote:
>> Thu, 02 Oct 2008 15:03:42 -0500,
>> Andrei Alexandrescu wrote:
>>> Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... any ideas? :o)
>>>
>>> One goal is to fix opIndexAssign and make it work similar to the way it works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test bed.
>>
>> What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?
> 
> Probably performance.
> 
> Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1.
> 
> But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c).
> 
> (The same idea can be extended to properties and .opSlice() )

Glad you brought opIndexAddAssign up. I think a good solution would avoid adding all opIndexXxxAssign functions. I think even opXxxAssign are undesirable.

Andrei
October 03, 2008
Fri, 03 Oct 2008 20:59:54 +0800,
KennyTM~ wrote:
> Sergey Gromov wrote:
> > What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?
> 
> Probably performance.
> 
> Consider seeking to the end of a 100M-node single-linked list, and increase its content by 1.
> 
> But I agree that if something like .opIndexAddAssign() is not defined, the compiler should fall back to use a.opIndexAssign(b, a.opIndex(b)+c).
> 
> (The same idea can be extended to properties and .opSlice() )

No, if you want performance in this particular case you define

ref int opIndex()

because I think whenever compiler encounters a[x]++ it should first test whether a[x] is an lvalue and if yes use it accordingly.  And only if it is not it should fall back to .opIndexAddButProbablyNotAssign() special overloads.
October 03, 2008
Fri, 03 Oct 2008 09:02:07 -0500,
Andrei Alexandrescu wrote:
> Sergey Gromov wrote:
> > Thu, 02 Oct 2008 15:03:42 -0500,
> > Andrei Alexandrescu wrote:
> >> Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... any ideas? :o)
> >>
> >> One goal is to fix opIndexAssign and make it work similar to the way it works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test bed.
> > 
> > What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?
> 
> One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.
> 
> Another problem (assuming the above is fixed) is that b will be looked up twice in the hash.

The latter is not a problem if you always try to use a[b] as an lvalue before trying anything more specific.  Though it makes the former even harder to fix.  Probably there should be another opIndex for a context where the user expects a non-existent element to be created:

ref T opIndexCreate(size_t i)

When compiler sees "a[b] += c" it first calculates the type of "a[b]" in an assignment context.  In case of indexing it means considering a.opIndexCreate, then a.opIndex, and finally the built-in indexing. Then it checks whether that type is an lvalue or implements the requested assignment operation.  If neither is true then compiler falls back to a.opIndexAssign(b, ...) as a special backward-compatibility case.
October 03, 2008
Sergey Gromov wrote:
> Fri, 03 Oct 2008 09:02:07 -0500,
> Andrei Alexandrescu wrote:
>> Sergey Gromov wrote:
>>> Thu, 02 Oct 2008 15:03:42 -0500,
>>> Andrei Alexandrescu wrote:
>>>> Yah, overloaded ops are due for an overhaul. I'm almost afraid to ask... any ideas? :o)
>>>>
>>>> One goal is to fix opIndexAssign and make it work similar to the way it works in arrays, e.g. a[b] += c. Indexing into hash tables is a good test bed.
>>> What's wrong with a.opIndexAssign(b, a.opIndex(b) + c)?
>> One problem is that for a hashtable that does not have b yet, opIndex will throw an exception.
>>
>> Another problem (assuming the above is fixed) is that b will be looked up twice in the hash.
> 
> The latter is not a problem if you always try to use a[b] as an lvalue before trying anything more specific.  Though it makes the former even harder to fix.  Probably there should be another opIndex for a context where the user expects a non-existent element to be created:
> 
> ref T opIndexCreate(size_t i)
> 
> When compiler sees "a[b] += c" it first calculates the type of "a[b]" in an assignment context.  In case of indexing it means considering a.opIndexCreate, then a.opIndex, and finally the built-in indexing.  Then it checks whether that type is an lvalue or implements the requested assignment operation.  If neither is true then compiler falls back to a.opIndexAssign(b, ...) as a special backward-compatibility case.

Yah, Walter, Bartosz and I discussed this (under the name opIndexLvalue). It does have a problem with sparse arrays. In a sparse array, a[b] = 0 means the array should actually erase slot at position b (or not insert it if it was missing in the first place). This suggests that a more flexible mechanism would be to have a place where the container, the index, and the assigned value are all available to the same function.

Andrei