December 06, 2010
On Mon, 06 Dec 2010 08:47:33 -0500, Jason House <jason.james.house@gmail.com> wrote:

> Jonathan M Davis Wrote:
>
>> On Sunday 05 December 2010 23:59:58 so wrote:
>> > On Mon, 06 Dec 2010 02:51:32 +0200, Michel Fortin
>> >
>> > <michel.fortin@michelf.com> wrote:
>> > > <http://d.puremagic.com/issues/show_bug.cgi?id=5325>
>> >
>> > Great!
>> >
>> > > See the strange predicate for the const(Object) version? That's  
>> because
>> > > opCmp() in Object doesn't work with const.
>> >
>> > For the same reason opEquals acting funny with const?
>>
>> Neither are marked as const on Object itself, and that needs to happen for
>> Object to be const correct. toHash() and toString() (or writeFrom() or whatever
>> it's going to become) have the same problem. None of them are const in Object.
>>
>> - Jonathan M Davis
>
> If object did that, things deriving from Object could not be lazy or memoize. It occurs to me that things deriving from object should be able to use const-correct functions and still compile. I don't have a D compiler handy or I'd see how well dmd handles that. I'd expect changing an input argument from T to const(T) should work since it's expanding the range of valid inputs. Similarly, the method itself should be able to become const since that too is expanding the valid input (to the implicit first argument).

It doesn't work.  obj == obj calls .opEquals(obj, obj), whose signature is:

bool opEquals(Object obj1, Object obj2)

So even if you mark a derived instance's opEquals as const, the derived class is stripped back down to Object before any calls are made.

Memoization and lazy calculations are not as important as being able to compare const or immutable objects.

-Steve
December 06, 2010
> It doesn't work.  obj == obj calls .opEquals(obj, obj), whose signature is:
>
> bool opEquals(Object obj1, Object obj2)
>
> So even if you mark a derived instance's opEquals as const, the derived class is stripped back down to Object before any calls are made.
>
> Memoization and lazy calculations are not as important as being able to compare const or immutable objects.
>
> -Steve

This one is a very serious issue.
Should changing their signatures to:
bool op###(auto ref const Object) const // breaking change but just signature

and addressing "auto ref" enough to fix it?

A few reports and Andrei's comment indicates that there is some misunderstanding(?) on semantics/implementation.
http://d.puremagic.com/issues/show_bug.cgi?id=4215#c4
http://d.puremagic.com/issues/show_bug.cgi?id=4668
http://d.puremagic.com/issues/show_bug.cgi?id=4258

With a glance to source code, looks like all that is required is just a few lines of change.

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
December 06, 2010
On Mon, 06 Dec 2010 10:11:30 -0500, so <so@so.do> wrote:

>> It doesn't work.  obj == obj calls .opEquals(obj, obj), whose signature is:
>>
>> bool opEquals(Object obj1, Object obj2)
>>
>> So even if you mark a derived instance's opEquals as const, the derived class is stripped back down to Object before any calls are made.
>>
>> Memoization and lazy calculations are not as important as being able to compare const or immutable objects.
>>
>> -Steve
>
> This one is a very serious issue.
> Should changing their signatures to:
> bool op###(auto ref const Object) const // breaking change but just signature
>
> and addressing "auto ref" enough to fix it?

auto ref doesn't matter, these are classes, they are always ref ;)

All that is needed is to make opEquals const (and its arguments const) as you say.

> With a glance to source code, looks like all that is required is just a few lines of change.

It should be relatively painless.  Just change the signatures of the base functions, and fix any compile errors.

-Steve
December 06, 2010
On 2010-12-06 00:16:27 -0500, Graham St Jack <Graham.StJack@internode.on.net> said:

> First, I have to say that it is wonderful that someone is taking a serious look at this area again, and even better, you have come up with a compiler patch to make it happen!
> 
> Some questions (assuming your patch or something like it gets into dmd):
> 
> Does this mean that I would be able to write this:
> immutable(Foo)ref foo; // create a reference
> foo = new immutable(Foo)(); // re-bind it (not sure about "new" syntax for immutables)

That's the whole point yes. This syntax works with my patch. :-)


> Are there any other show-stopping syntax issues that are holding up widespread adoption/rollout of const-correctness?

Surly there are others issues to solve. But this one is the one I kept falling into when trying to use immutable objects in my code some time ago.


> What do Walter and Andrei think?

That I'd like to know.


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

December 06, 2010
Actually I really like the optional usage of ref here because it might help to disambiguate between a class and a struct object:

E.g.:

class Foo
{
}

struct Bar
{
}

void main()
{
    Foo ref foo;  // I can be sure Foo is a class
    Bar bar;
}

Sometimes it happens that I forget to 'new' a class object, and to the naked eye the code doesn't appear wrong, but using the optional ref keyword helps in tracking this kind of bug down. I guess the compiler should throw an error in cases where Foo is a struct.
December 06, 2010
On 2010-12-06 13:35:27 -0500, Andrej Mitrovic <andrej.mitrovich@gmail.com> said:

> Actually I really like the optional usage of ref here because it might
> help to disambiguate between a class and a struct object:
> 
> E.g.:
> 
> class Foo
> {
> }
> 
> struct Bar
> {
> }
> 
> void main()
> {
>     Foo ref foo;  // I can be sure Foo is a class
>     Bar bar;
> }
> 
> Sometimes it happens that I forget to 'new' a class object, and to the
> naked eye the code doesn't appear wrong, but using the optional ref
> keyword helps in tracking this kind of bug down. I guess the compiler
> should throw an error in cases where Foo is a struct.

It does (throw an error in cases where Foo is a struct). I never though of this usage. :-)

Since we're speaking of the optional use of 'ref', here's a little quiz:

	alias Object A;
	alias Object ref B;

	A ref a;
	B ref b;

What happens here? Should there be an error somewhere? Where? Also, what happens if we apply different modifiers at different places?


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

December 06, 2010
On Mon, 06 Dec 2010 13:44:41 -0500, Michel Fortin <michel.fortin@michelf.com> wrote:

> On 2010-12-06 13:35:27 -0500, Andrej Mitrovic <andrej.mitrovich@gmail.com> said:
>
>> Actually I really like the optional usage of ref here because it might
>> help to disambiguate between a class and a struct object:
>>  E.g.:
>>  class Foo
>> {
>> }
>>  struct Bar
>> {
>> }
>>  void main()
>> {
>>     Foo ref foo;  // I can be sure Foo is a class
>>     Bar bar;
>> }
>>  Sometimes it happens that I forget to 'new' a class object, and to the
>> naked eye the code doesn't appear wrong, but using the optional ref
>> keyword helps in tracking this kind of bug down. I guess the compiler
>> should throw an error in cases where Foo is a struct.
>
> It does (throw an error in cases where Foo is a struct). I never though of this usage. :-)
>
> Since we're speaking of the optional use of 'ref', here's a little quiz:
>
> 	alias Object A;
> 	alias Object ref B;
>
> 	A ref a;
> 	B ref b;
>
> What happens here? Should there be an error somewhere? Where? Also, what happens if we apply different modifiers at different places?

alias Object ref B should be the same as alias Object B, since Object ref is the same type as Object.

So there should be no error.

Now, what happens for this:

alias const(Object) ref A;

A ref a;

My gut says this should be the same error as trying to ref a struct.  But I can see it also being allowed.

-Steve
December 06, 2010
On Monday, December 06, 2010 05:41:42 Steven Schveighoffer wrote:
> On Mon, 06 Dec 2010 04:44:07 -0500, spir <denis.spir@gmail.com> wrote:
> > On Mon, 6 Dec 2010 00:31:41 -0800
> > 
> > Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> >> toString() (or writeFrom() or whatever
> >> it's going to become)
> > 
> > guess it was writeTo() ;-) but "writeFrom" is nice as well, we should
> > find some useful use for it
> 
> It was proposed as writeTo, but I'm not opposed to a different name.

I have no problem with writeTo(). I just couldn't remember what it was and didn't want to take the time to look it up, and the name isn't as obvious as toString(), since it's not a standard name which exists in other languages, and it isn't actually returning anything. Whether it's to or from would depend on how you look at it - to the given delegate or from the object. But writeTo() is fine. Once it's used, it'll be remembered.

> BTW, the proposal does properly mark writeTo as const.

Good. It figures that you'd remember given your general take on const.

- Jonathan M Davis
December 06, 2010
On Monday, December 06, 2010 07:37:27 Steven Schveighoffer wrote:
> On Mon, 06 Dec 2010 10:11:30 -0500, so <so@so.do> wrote:
> >> It doesn't work.  obj == obj calls .opEquals(obj, obj), whose signature
> >> is:
> >> 
> >> bool opEquals(Object obj1, Object obj2)
> >> 
> >> So even if you mark a derived instance's opEquals as const, the derived class is stripped back down to Object before any calls are made.
> >> 
> >> Memoization and lazy calculations are not as important as being able to compare const or immutable objects.
> >> 
> >> -Steve
> > 
> > This one is a very serious issue.
> > Should changing their signatures to:
> > bool op###(auto ref const Object) const // breaking change but just
> > signature
> > 
> > and addressing "auto ref" enough to fix it?
> 
> auto ref doesn't matter, these are classes, they are always ref ;)
> 
> All that is needed is to make opEquals const (and its arguments const) as
> you say.
> 
> > With a glance to source code, looks like all that is required is just a few lines of change.
> 
> It should be relatively painless.  Just change the signatures of the base functions, and fix any compile errors.

And watch all the code break... I'll definitely welcome the change (it's probably the first bug that I voted on in bugzilla), but there will be tons of code broken by it, since you just _know_ that a lot of user code doesn't bother to make toString() and its friends const. It's still a change that needs to be made though. Being unable to properly compare const and immutable objects is crippling. Of course, it would be more of an issue if it were easier to actually create immutable objects which are classes rather than strcuts, but that's a separate issue.

- Jonathan M Davis
December 06, 2010
On Mon, 06 Dec 2010 14:04:43 -0500, Jonathan M Davis <jmdavisProg@gmx.com> wrote:

> On Monday, December 06, 2010 07:37:27 Steven Schveighoffer wrote:

>> It should be relatively painless.  Just change the signatures of the base
>> functions, and fix any compile errors.
>
> And watch all the code break... I'll definitely welcome the change (it's probably
> the first bug that I voted on in bugzilla), but there will be tons of code broken
> by it, since you just _know_ that a lot of user code doesn't bother to make
> toString() and its friends const. It's still a change that needs to be made
> though. Being unable to properly compare const and immutable objects is
> crippling. Of course, it would be more of an issue if it were easier to actually
> create immutable objects which are classes rather than strcuts, but that's a
> separate issue.

Yes, one of the issues is that const has a viral effect.  If you ignore const, none of your functions are const.  So if you use functions inside opEquals, those have to be marked as const, and so on.

But there are very few circumstances where opEquals needs to be marked as mutable.  If that is the case, there is something broken with your code (and you should fix it) or there's something wrong with code you use (and you'll have to insert casts to deal with it for now).  In a very small number of circumstances, non-const opEquals can be beneficial (such as caching data that is expensive to calculate).  We need to find another way to deal with that (I suggest having logical const, but that's not a popular idea with Walter :).  But it shouldn't detract from the requirements.  You can always circumvent if you need special cases.  I'd rather start from a solid const-supporting position and add holes where they make sense then start from a base that is full of holes and try to patch them slowly.

In many cases affixing const to your code is not just a simple 'slap a const on here'.  It can involve careful thought and possibly redesign.

But without these changes, const is very useless, the standard library needs to eat its own dogfood if we want to peddle it to others.

-Steve