mutable, const, immutable guidelines

Daniel Davidson nospam at spam.com
Wed Oct 9 05:58:58 PDT 2013


On Wednesday, 9 October 2013 at 04:31:55 UTC, Ali Çehreli wrote:
> On 10/08/2013 03:12 PM, qznc wrote:
>
> > On Monday, 7 October 2013 at 17:57:11 UTC, Ali Çehreli wrote:
> >> To look at just one usage example, the following line
> carries two
> >> requirements:
> >>
> >>     auto a = T();
> >>     immutable b = a;
> >>
> >> 1) b will be an immutable copy of a.
> >>
> >> 2) T will always be usable as in that fashion.
> >>
> >> If T appears on an API, it is the responibility of the user
> to ensure
> >> whether they are allowed to treat T in that way. Otherwise,
> they risk
> >> maintainability if the module decides to change T in any way
> that fits
> >> the module's needs. If they have not yet advertised that T
> can be used
> >> as immutable, it should not be.
> >
> > I do not agree with you, that the user has the responsibility.
>
> I have difficulty agreeing with myself as well. :) However, the 
> power of immutable makes me think so. Interestingly, once a 
> user creates an immutable variable of a type, that type must 
> support that use.
>
> > Rather I
> > think the provider of T has the responsibility to maintain
> backwards
> > compatibility.
>
> Agreed but I don't know how. Here is challenge: Let's start 
> with the following program:
>
> // Library type
> struct MyInt
> {
>     int i;
> }
>
> void main()
> {
>     // User code
>     auto a = MyInt(1);
>     immutable b = a;
> }
>
> Let's assume that the library adds a private dynamic array of 
> ints to that type:
>
> // Library type
> struct MyInt
> {
>     int i;
>     private int[] history;    // <-- Added
> }
>
> void main()
> {
>     // User code
>     auto a = MyInt(1);
>     immutable b = a;          // <-- Existing code breaks
> }
>
> Error: cannot implicitly convert expression (a) of type MyInt 
> to immutable(MyInt)
>
> Apparently, the library made a change that made its type 
> non-immutable-able. :p What is missing in MyInt? How should it 
> be defined instead of simply adding 'history'?
>

The example is great. I would say it like "Apparently the library 
made a change that added mutable aliasing and therefore broke the 
special logic the compiler applies to types without aliasing". I 
think the root cause is the language special casing structs with 
no mutable aliasing by allowing the "immutable b = a" to work in 
the first place. One step toward a solution would be the 
existence of a generalized dup. Then calls to "immutable b = a" 
with mutable aliasing could lower to a call to the generalized 
dup, causing a deep copy and therefore still allowing assignment 
from non-immutable to immutable. This has its own set of problems 
- like what if the library designer wants sharing and not deep 
copies (perhaps they employ copy on write semantics on their own).

What are other solutions or ways to prevent this jump from no 
aliasing to mutable aliasing breaking code? Maybe assume mutable 
aliasing on all your structs from the start, then those 
statements like "immutable b = a" won't appear in the first place.

struct T {
   ...
   version(unittest) int[] _justToAddMutableAliasing;
}

Now you know that assignment of "immutable b = a" will fail in 
unittests. But this is crazy and it would only help your users if 
they run unittests.

>
> Technically, MyInt is in a breaking state because the user used 
> it as immutable. One way to look at this situation is to 
> observe that the user took some freedom of the library just by 
> using its type as immutable.
>

I don't quite understand the terminology "using it's type as 
immutable". I think the breaking state comes from copying in 
general having two flavors - shallow and deep. Transitive deep 
copy always offers the option of immutable source and target. 
Shallow copy also offers the option of immutable source and 
target, as long as the type has no mutable aliasing, simply 
because a shallow copy is also a deep copy if no aliasing. And, 
shallow copy does not offer the option of immutable in the face 
of types with mutable aliasing. It is when types go from having 
no mutable aliasing to having mutable aliasing (and the reverse) 
that the rules of the compiler change the game.

> > Unfortunately, there is no way for the provider to allow or
> disallow
> > immutable(T). Maybe there should be a UDA or something for
> this?
>

One way might be to disallow copy and assignment (is this a D 
capability?) and provide pure factory that only returns 
immutable(T).

> I think the language is lacking tools to support the use case 
> above.
>

I think if the default assignment and copy constructor for 
structs with aliasing were automatic transitive deep copy this 
issue would disappear. Other issues would come up. Also, I'm 
pretty sure Walter is not a fan of this concept because he does 
not advocate copying any data in postblits.

> Ali



More information about the Digitalmars-d-learn mailing list