July 11, 2012
On 11/07/2012 21:45, Steven Schveighoffer wrote:
> On Wed, 11 Jul 2012 15:14:19 -0400, deadalnix <deadalnix@gmail.com> wrote:
>
>> On 11/07/2012 20:53, Steven Schveighoffer wrote:
>>> On Wed, 11 Jul 2012 14:21:24 -0400, Steven Schveighoffer
>>> <schveiguy@yahoo.com> wrote:
>>>
>>>
>>>> It also seems to allow abuses. For example:
>>>>
>>>> class A
>>>> {
>>>> private int _x;
>>>> public @property x() const { return _x; }
>>>> }
>>>>
>>>> class B : A
>>>> {
>>>> private int _x2;
>>>> public @property x() { return _x2++; }
>>>> }
>>>>
>>>
>>> Another abuse:
>>>
>>> const(B) b = new B;
>>> // auto bx = b.x; // oops, compiler error
>>> const(A) a = b;
>>> auto bx = a.x;
>>>
>>> -Steve
>>
>> This shouldn't be a compiler error. An object of type B is 100% of
>> time mutable now.
>
> According to my code, b is const, and never was mutable.
>
> If you are saying that I should be able to call b.x, then this proposal
> is even worse than I thought! I have no idea what const means in this
> world.
>
> -Steve

Const is a bridge between mutable and immutable world. The guarantee proposed with const is that it never mutate an immutable data. This is slightly different than how const is implemented actually, but still ensure that no immutable data is muted.
July 11, 2012
On Wednesday, July 11, 2012 13:46:17 Andrei Alexandrescu wrote:
> I don't think they should be pure. Do you have reasons to think otherwise?

As I understand it, Walter's current plan is to require that opEquals, opCmp, toString, and toHash be @safe const pure nothrow - for both classes and structs.

- Jonathan M Davis
July 12, 2012
On Wednesday, 11 July 2012 at 18:21:24 UTC, Steven Schveighoffer wrote:
> On Wed, 11 Jul 2012 14:01:44 -0400, deadalnix <deadalnix@gmail.com> wrote:
>
>> On 11/07/2012 19:49, Andrei Alexandrescu wrote:
>>> On 7/11/12 1:40 PM, Jakob Ovrum wrote:
>>>> Some classes don't lend themselves to immutability. Let's take something
>>>> obvious like a class object representing a dataset in a database. How is
>>>> an immutable instance of such a class useful?
>>>
>>> This is a good point. It seems we're subjecting all classes to certain
>>> limitations for the benefit of a subset of those classes.
>>>
>>> Andrei
>>
>> Did you saw the proposal of feep/tgehr on #d ?
>>
>> It basically state that you can overload a const method with a non const one if :
>>  - You don't mutate any data that belong to the parent.
>>  - You are prevented to create any immutable instance of that classe or any subclasse.
>
> I don't like this idea.  It means you could not use pure functions to implicitly convert mutable class instances to immutable (something that should be possible today).

I do like the idea. Please explain by example why a pure function could no longer convert mutable class instances to immutable? The proposal to restrict the use of immutable is only supposed to affect classes that specifically request it.

> It also seems to allow abuses.  For example:
>
> class A
> {
>    private int _x;
>    public @property x() const { return _x; }
> }
>
> class B : A
> {
>    private int _x2;
>    public @property x() { return _x2++; }
> }

I think you would have to mark B somehow to indicate that immutable(B) is now illegal, e.g.

@mutating class B : A
{
   private int _x2;
   public @property override x() { return _x2++; }
}

> Now I've completely changed the logistics of the x property so that it's essentially become mutable.

This kind of perversion is already possible when x() is const. x() is allowed to mutate and return a static or global variable.
July 12, 2012
>> 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.
>
> I think a caching solution would cover most valid needs and indeed would be checkable.
>
> We can even try its usability with a library-only solution. The idea is to plant a mixin inside the object that defines a static hashtable mapping addresses of objects to cached values of the desired types. The destructor of the object removes the address of the current object from the hash (if there).
>
> Given that the hashtable is global, it doesn't obey the regular rules for immutability, so essentially each object has access to a private stash of unbounded size. The cost of getting to the stash is proportional to the number of objects within the thread that make use of that stash.
Uh, it better not be proportional. Hashtable gives us O(1), one hopes.

> Sample usage:
>
> class Circle {
>     private double radius;
>     private double circumferenceImpl() const {
>         return radius * 2 * pi;
>     }
>     mixin Cached!(double, "circumference", circumferenceImpl);
>     ...
> }
>
> auto c = new const(Circle);
Aside: what's the difference between this and new immutable(Circle)?

> double len1 = c.circumference;
> double len2 = c.circumference;
>
> Upon the first use of property c.circumference, Lazy computes the value by calling this.circumferenceImpl() and stashes it in the hash. The second call just does a hash lookup.
>
> In this example searching the hash may actually take longer than computing the thing, but I'm just proving the concept.
>
> If this is a useful artifact, Walter had an idea a while ago that we can have the compiler help by using the per-object monitor pointer instead of the static hashtable. Right now the pointer points to a monitor object, but it could point to a little struct containing e.g. a Monitor and a void*, which opens the way to O(1) access to unbounded cached data. The compiler would then "understand" to not consider that date regular field accesses, and not make assumptions about them being immutable.
>
> Any takers for Cached? It would be good to assess its level of usefulness first.

I like this idea, and I suspect it could be used to implement not just caching but lazy immutable data structures.

Except that I don't see why Cached!(...) needs to physically separate the mutable state from the rest of the object. I mean, I see that Cached!(...) would have to cast away immutable (break the type system) in order to put mutable state in an immutable object, but if we set aside the current type system for a moment, *in principle* what's the big deal if the mutable state is physically located within the object? In many cases you can save significant time and memory by avoiding all that hashtable management, and performance Nazis like me will want that speed (when it comes to standard libraries, I demand satisfaction).

Now, I recognize and respect the benefits of transitive immutability:
1. safe multithreading
2. allowing compiler optimizations that are not possible in C++
3. ability to store compile-time immutable literals in ROM

(3) does indeed require mutable state to be stored separately, but it doesn't seem like a common use case (and there is a workaround), and I don't see how (1) and (2) are necessarily broken.

As a separate question, do you think it possible to implement Cached!(...) to access an immutable field by casting away immutable, without screwing up (1) and (2)?

July 12, 2012
> Except that I don't see why Cached!(...) needs to physically separate the mutable state from the rest of the object. I mean, I see that Cached!(...) would have to cast away immutable (break the type system) in order to put mutable state in an immutable object, but if we set aside the current type system for a moment, *in principle* what's the big deal if the mutable state is physically located within the object? In many cases you can save significant time and memory by avoiding all that hashtable management, and performance Nazis like me will want that speed (when it comes to standard libraries, I demand satisfaction).
>
> Now, I recognize and respect the benefits of transitive immutability:
> 1. safe multithreading
> 2. allowing compiler optimizations that are not possible in C++
> 3. ability to store compile-time immutable literals in ROM
>
> (3) does indeed require mutable state to be stored separately, but it doesn't seem like a common use case (and there is a workaround), and I don't see how (1) and (2) are necessarily broken.

I must be tired.

Regarding (1), right after posting this I remembered the difference between caching to a "global" hashtable and storing the cached value directly within the object: the hashtable is thread-local, but the object itself may be shared between threads. So that's a pretty fundamental difference.

Even so, if Cached!(...) puts mutable state directly in the object, fast synchronization mechanisms could be used to ensure that two threads don't step on each other, if they both compute the cached value at the same time. If the cached value is something simple like a hashcode, an atomic write should suffice. And both threads should compute the same result so it doesn't matter who wins.
July 12, 2012
"David Piepgrass" , dans le message (digitalmars.D:172007), a écrit :
> @mutating class B : A
> {
>     private int _x2;
>     public @property override x() { return _x2++; }
> }

A fun() pure;

You can't cast the result of fun to immutable, because it may be a B instance.
July 12, 2012
"David Piepgrass" , dans le message (digitalmars.D:172009), a écrit :
>> Now, I recognize and respect the benefits of transitive
>> immutability:
>> 1. safe multithreading
>> 2. allowing compiler optimizations that are not possible in C++
>> 3. ability to store compile-time immutable literals in ROM
>>
>> (3) does indeed require mutable state to be stored separately, but it doesn't seem like a common use case (and there is a workaround), and I don't see how (1) and (2) are necessarily broken.
> 
> I must be tired.
> 
> Regarding (1), right after posting this I remembered the difference between caching to a "global" hashtable and storing the cached value directly within the object: the hashtable is thread-local, but the object itself may be shared between threads. So that's a pretty fundamental difference.
> 
> Even so, if Cached!(...) puts mutable state directly in the object, fast synchronization mechanisms could be used to ensure that two threads don't step on each other, if they both compute the cached value at the same time. If the cached value is something simple like a hashcode, an atomic write should suffice. And both threads should compute the same result so it doesn't matter who wins.

Yes. It is possible to write a library solution to compute a cached value by casting away const in a safe manner, even in a multithreaded environment. The limitation is that, if I'm not mistaken, this library solution cannot ensure it is not immutable (and potentially placed in ROM) when it is const, making the cast undefined.
July 12, 2012
"Jonathan M Davis" , dans le message (digitalmars.D:172005), a écrit :
> On Wednesday, July 11, 2012 13:46:17 Andrei Alexandrescu wrote:
>> I don't think they should be pure. Do you have reasons to think otherwise?
> 
> As I understand it, Walter's current plan is to require that opEquals, opCmp, toString, and toHash be @safe const pure nothrow - for both classes and structs.

And is the plan add each tag one by one, breaking codes in many places each time ?


July 12, 2012
On Thursday, July 12, 2012 07:31:02 Christophe Travert wrote:
> "Jonathan M Davis" , dans le message (digitalmars.D:172005), a écrit :
> > On Wednesday, July 11, 2012 13:46:17 Andrei Alexandrescu wrote:
> >> I don't think they should be pure. Do you have reasons to think otherwise?
> > 
> > As I understand it, Walter's current plan is to require that opEquals, opCmp, toString, and toHash be @safe const pure nothrow - for both classes and structs.
> 
> And is the plan add each tag one by one, breaking codes in many places each time ?

I don't know. Implementation issues have prevented it from happening yet. I believe that some additional requirements were already added (e.g. toHash on classes now requires @safe IIRC), but for the most part, it's simply planned, and I don't know that it's been fully sorted out how it's going to be rolled out.

However, based on Andrei's new thread, "All right, all right! Interim decision regarding qualified Object methods," it looks like this thread convinced Andrei and Walter to completely revisit how all of this works - including whether Object even needs these functions, thanks to your post on that. So, the whole rollout of making those functions have to be @safe const pure nothrow has now presumably been tabled if not outright scrapped.

- Jonathan M Davis
July 12, 2012
On Wed, 11 Jul 2012 17:20:08 -0400, deadalnix <deadalnix@gmail.com> wrote:

> Const is a bridge between mutable and immutable world. The guarantee proposed with const is that it never mutate an immutable data. This is slightly different than how const is implemented actually, but still ensure that no immutable data is muted.

No.  Const is a contract saying the function will not modify the data given to it via that parameter.  It's useful when reasoning about code.

When you can modify data passed via a const pointer the entire concept of const becomes convention.

-Steve