View mode: basic / threaded / horizontal-split · Log in · Help
July 11, 2012
Re: Congratulations to the D Team!
On Wednesday, 11 July 2012 at 02:55:33 UTC, Jakob Ovrum wrote:
> On Wednesday, 11 July 2012 at 02:33:53 UTC, Jonathan M Davis 
> wrote:
>> -snip-
>>
>> So, with that, we can have const work wonderfully without 
>> requiring it, even
>> if it does take a bit of work to get around it with classes. 
>> So, what am I
>> missing here? Why doesn't this work? Or has Walter just not 
>> properly
>> considered this option?
>>
>> - Jonathan M Davis
>
> This is exactly the kind of balance I am hoping we can 
> implement. I think Hara Kenji suggested something very similar 
> at one point, but it had some kind of problem with it. It would 
> be awesome if Kenji could provide some insight on that. I think 
> his proposal is somewhere on Github, I'll have a look.

The relevant discussion containing the proposal is here:

    https://github.com/D-Programming-Language/phobos/pull/262

I was wrong; I don't see anyone having found any flaws with the 
final proposal.

And while the proposal wasn't explicitly accepted nor rejected, 
it doesn't look like it found its way into the final pull request:

    https://github.com/D-Programming-Language/druntime/pull/72
July 11, 2012
Re: Congratulations to the D Team!
On Wed, Jul 11, 2012 at 04:59:28AM +0200, Jakob Ovrum wrote:
> On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu
> wrote:
> >On 7/10/12 9:45 PM, Timon Gehr wrote:
> >>I do not desire logical const as a language feature. But
> >>conservative type systems are not good for everything. The root of
> >>the class hierarchy needs to be good for everything. Object is not
> >>an adequate root any more.
> >
> >How about we consider just stiffening that upper lip and implement
> >comparison and hashing without modifying their target?
> >
> >Andrei
> 
> It's more likely to go down like this: programmer attempts to write
> his opEquals (or toString etc) within the restrictions of const, but
> fails due to the requirements of the implementation (which can
> easily go beyond simple performance measures like caching, as
> demonstrated). The programmer then writes his own mutable member
> function and neglects opEquals altogether. If the programmer is real
> nice, he/she will write a throwing opEquals stub.

This is exactly what I was saying. All that beautiful, pristine, perfect
infrastructure we're building in druntime eventually just gets
sidestepped, because it is unable to cater for what the programmer
needs, and so the programmer ends up reimplementing his own
infrastructure, over and over again. I can't see how that is beneficial.


T

-- 
What doesn't kill me makes me stranger.
July 11, 2012
Re: Inherited const when you need to mutate
On 10/07/12 19:13, H. S. Teoh wrote:
> On Tue, Jul 10, 2012 at 06:48:51PM +0200, Timon Gehr wrote:
>> On 07/10/2012 06:45 PM, H. S. Teoh wrote:
>>> Yeah, this is logical const. Unfortunately, D doesn't have logical
>>> const.
>>>
>>
>> Then why on earth is druntime acting as if it does?
>
> Y'know, this brings up an interesting question. Do methods like toString
> _need_ to be const? That is, _physical_ const?  Or are we unconsciously
> conflating physical const with logical const here?
>
> Yes, certain runtime operations need to be able to work with const
> methods, but I wonder if those required const methods really belong to a
> core set of more primitive operations that guarantee physical const, and
> perhaps shouldn't be conflated with logical operations like "convert
> this object to a string representation", which _may_ require caching,
> etc.?
>
> Or perhaps application code want to be defining their own non-const
> versions of certain methods so that they can do whatever they need to do
> with logical const, without worrying about breaking physical const-ness.
>
> I'm starting to think that D's hardline approach to const is clashing
> with the principle of information hiding. Users of a class shouldn't
> _need_ to know if an object is caching the value of toString, toHash, or
> whatever it is. What they care for is that the object doesn't visibly
> change, that is, logical const. Binary const implies logical const, but
> the implication doesn't work the other way round. While it's nice to
> have binary const (strong, enforceable guarantee), it breaks
> encapsulation: just because a class needs to do caching, means its
> methods can't be const, and this is a visible (and viral, no less)
> change in its external API. What should just be an implementation detail
> has become a visible difference to the outside world -- encapsulation is
> broken.
>
> I don't know how to remedy this. It's clear that physical const does
> have its value -- it's necessary to properly support immutable, allows
> putting data in ROM, etc.. But it's also clear that something is missing
> from the picture. Implementation details are leaking past object APIs,
> caching and other abstractions can't work with const, etc., and that's
> not a good thing.
>
>
> T

I think you're right.
Something I wonder about, though, is how many different use cases are we 
dealing with?

Suppose we had a caching solution (you could think of it as @cached, but 
it could be done in a library). The user would need to provide a const, 
pure function which returns the same value that is stored in the cache.
This is enforceable. The only way to write to the cache, is by calling 
the function.

How far would that take us? I don't think there are many use cases for 
logically pure, apart from caching, but I have very little idea about 
logical const.
July 11, 2012
Re: Congratulations to the D Team!
On 2012-07-11 02:03, Walter Bright wrote:

> So do I. But all language design involves tradeoffs. I believe the
> advantages of const toHash etc. outweigh the disadvantage of less
> straightforward memoization.

Can't there be non-const versions of these methods as well?

-- 
/Jacob Carlborg
July 11, 2012
Re: Inherited const when you need to mutate
On 2012-07-11 02:20, Timon Gehr wrote:

> I for one would be satisfied if inheriting from object became optional:
>
> // object.di
> class RawObject /+ this is the root of the class hierarchy +/{ }
> class SynchronizableObject : RawObject { void* monitor; }
> class Object : SynchronizableObject {
> const { stuff }
> }
>
> // user code
> class NoCruft : RawObject {
> // ...
> }
>

Ruby 1.9 is doing something similar.

class BasicObject
end

class Object < BasicObject
end

class Foo # inherits from Object
end

class Bar < BasicObject # inherits from BasicObject
end

I wonder if the same can be done, with the now base class protection, in D:

class RawObject : private Object
{}

class Foo : RawObject
{}

-- 
/Jacob Carlborg
July 11, 2012
Re: Inherited const when you need to mutate
Andrei Alexandrescu , dans le message (digitalmars.D:171828), a écrit :
> On 7/10/12 5:19 PM, H. S. Teoh wrote:
> 
> There is value in immutable objects that has been well discussed, which 
> is incompatible with logical constness. We can change the language such 
> as: a given type X has the option to declare "I want logical const and 
> for that I'm giving up the possibility of creating immutable(X)". That 
> keeps things proper for everybody - immutable still has the strong 
> properties we know and love, and such types can actually use logical const.
> 
> A number of refinements are as always possible.

I think this is a good idea, but for classes, inheritance is an issue. 

Example:

class A
{
 int a;
 int compute() const pure { return a; }
 final int fun() const pure
 {
   a.compute; // optimised out by the compiler
   return a.compute;
 }
}

class B : A
{
 @mutable int b;
 override int compute() const pure
 {
    if(!b) b = longComputation(a);
    // a += 1; // error, a is bitwise-const
    return b; // mutating and returning a mutable part at the 
              // programmer's risk
 }
}

A.compute is bitwise const. However B.compute is logical const. A.fun is 
bitwise const, and can be optimised. But that is no longer true with a B 
instance. However, the compiler must be able to make those 
optimizations, otherwise all the power of const for any non final object 
is lost, because someone may derive a logical const class. This means 
the programmer is *responsible* for creating a logical const-behavior. 
This is a serious issue.

Given the system-programming aspect of D, I would say the programmer 
should be allowed to do such a thing, taking the risk to have an 
undefined behavior. But with great caution. At least, it will be less 
dangerous than casting away const. Just providing a way to make it 
impossible to create an immutable instance of some classes would make it 
less dangerous to cast away constness.

-- 
Christophe
July 11, 2012
Re: Congratulations to the D Team!
On 7/10/12 10:22 PM, Walter Bright wrote:
> On 7/10/2012 6:53 PM, Andrei Alexandrescu wrote:
>> On 7/10/12 9:14 PM, Walter Bright wrote:
>>> Anyhow, the point of @trusted is to notify the maintainer that "here be
>>> dragons".
>>
>> I think that's not representing @trusted quite accurately. There's no
>> dragon
>> there. @trusted means "the code is correct but not mechanically
>> checkable".
>> Casting away const is NOT correct.
>
> You're right in that it would break immutable args passed.
>
> PIMPL is a better option.

I am pretty sure your approach with PIMPL is also undefined, so we may 
as well stop suggesting it as a viable possibility.

Consider:

immutable(T) t1 = create();
int x = t1.x;
const(T) t2 = t2;
t2.method();
assert(x == t1.x); // the compiler should be able to assume this

For the entire immutable thing to work, the compiler must have an 
iron-clad guarantee that transitively-accessed data members in t1 will 
preserve before and after the call to t2.method(). Your proposed cast, 
if ever defined, essentially breaks that guarantee. If that is down, the 
entire notion of immutable breaks down.

Please do not propose pimpl and cast anymore. This is important. Thanks.


Andrei
July 11, 2012
Re: Congratulations to the D Team!
On 7/10/12 10:33 PM, Jonathan M Davis wrote:
> Yeah. It seems to me that a reasonable approach would be to give Object two
> versions of opEquals, opCmp, toString, and toHash - a const and non-const
> version of each. The non-const version then calls the const version. So, the
> normal thing to do is have your class work with const and override the const
> version. If the non-const version on Object gets called, then the const
> version in the derived class gets called, and if the derived type is used
> directly, then it'll always use the const version, because that's what the
> derived type has.
>
> On the other hand, for a class which doesn't work with const, it does this:
>
> 1. Override the non-const versions of opEquals, opCmp, toString, and toHash,
> giving them the implementations which you want.
>
> 2. Override the const versions of opEquals, opCmp, toString, and toHash and
> make their bodies assert(0).
>
> That way, those 4 functions can be used normally as long as the object isn't
> assigned to a const reference, and if it is, then you get an Error.

I was a long-time proponent of this. It's less exciting than it may seem 
actually.

(a) Classes that work with const just fine incur one extra virtual call. 
I think this can be avoided by having the compiler plant the same 
pointer for the const and non-const version in the vtable.

(b) Classes that can't do as little as one of these four operations 
without mutating the object are completely excluded from the 
immutability system, even if they'd otherwise benefit from it. Even 
those that don't "care" they need to actively _work_ on not caring, 
which doesn't sit well.

So I don't see this as a viable solution to people who are fine with 
const, but would like to use e.g. some lazy computation.


Andrei
July 11, 2012
Re: Congratulations to the D Team!
On 7/10/12 10:59 PM, Jakob Ovrum wrote:
> On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu wrote:
>> On 7/10/12 9:45 PM, Timon Gehr wrote:
>>> I do not desire logical const as a language feature. But conservative
>>> type systems are not good for everything. The root of the class
>>> hierarchy needs to be good for everything. Object is not an adequate
>>> root any more.
>>
>> How about we consider just stiffening that upper lip and implement
>> comparison and hashing without modifying their target?
>>
>> Andrei
>
> It's more likely to go down like this: programmer attempts to write his
> opEquals (or toString etc) within the restrictions of const, but fails
> due to the requirements of the implementation (which can easily go
> beyond simple performance measures like caching, as demonstrated). The
> programmer then writes his own mutable member function and neglects
> opEquals altogether. If the programmer is real nice, he/she will write a
> throwing opEquals stub.

I gave evidence on a large, high quality C++ codebase that the use of 
mutable (which is the solution of choice for memoization, caching, and 
lazy computation) is extremely scarce.

What evidence do you have for your prediction?


Andrei
July 11, 2012
Re: Congratulations to the D Team!
On 7/11/12 12:59 AM, H. S. Teoh wrote:
> On Wed, Jul 11, 2012 at 04:59:28AM +0200, Jakob Ovrum wrote:
>> On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu
>> wrote:
>>> On 7/10/12 9:45 PM, Timon Gehr wrote:
>>>> I do not desire logical const as a language feature. But
>>>> conservative type systems are not good for everything. The root of
>>>> the class hierarchy needs to be good for everything. Object is not
>>>> an adequate root any more.
>>>
>>> How about we consider just stiffening that upper lip and implement
>>> comparison and hashing without modifying their target?
>>>
>>> Andrei
>>
>> It's more likely to go down like this: programmer attempts to write
>> his opEquals (or toString etc) within the restrictions of const, but
>> fails due to the requirements of the implementation (which can
>> easily go beyond simple performance measures like caching, as
>> demonstrated). The programmer then writes his own mutable member
>> function and neglects opEquals altogether. If the programmer is real
>> nice, he/she will write a throwing opEquals stub.
>
> This is exactly what I was saying. All that beautiful, pristine, perfect
> infrastructure we're building in druntime eventually just gets
> sidestepped, because it is unable to cater for what the programmer
> needs, and so the programmer ends up reimplementing his own
> infrastructure, over and over again. I can't see how that is beneficial.

How often do you need memoization? It's not even recognized by this 
mailer's editor.

Andrei
5 6 7 8 9 10 11 12 13
Top | Discussion index | About this forum | D home