mutable, const, immutable guidelines

Daniel Davidson nospam at spam.com
Wed Oct 16 11:49:51 PDT 2013


On Wednesday, 16 October 2013 at 17:55:14 UTC, H. S. Teoh wrote:
> On Wed, Oct 16, 2013 at 07:23:24PM +0200, Daniel Davidson wrote:
>> On Wednesday, 2 October 2013 at 13:09:34 UTC, Daniel Davidson 
>> wrote:
> [...]
>> >Maybe it is a philosophical question, but where does 
>> >immutability
>> >really come from? Is it an aspect of some piece of data or is 
>> >it a
>> >promise that function will not change it? Or is it a 
>> >requirement
>> >by a function that data passed not be changed by anyone else?
>
> I think it helps to realize that D's const system is different 
> from
> C++'s.
>
> Immutable means the data will never change, ever. It means you 
> can put
> that data in read-only memory, maybe burned into a ROM chip or 
> something
> like that.
>
> Const means *you* can't change the data, but somebody else may 
> be able
> to.
>
> Therefore:
>
>
> [...]
>> After trying for several days to use immutable with types 
>> containing
>> some mutable aliasing I have come to the conclusion that maybe 
>> rule
>> number one should be:
>
> If you have mutable aliasing, that means the data cannot be 
> immutable.
> Use const.
>
>
>> If you have a type that has now or may ever have in the future 
>> any
>> mutable aliasing (e.g. inclusion of T[] or T1[T1] where Ts are
>> mutable) do not ever use the immutable keyword in any context 
>> as
>> things just break down.
>
> Yes, because immutable means nothing, no one, can change the 
> data after
> it's constructed, ever. If you want mutable aliasing, what you 
> want is
> const, not immutable.
>

I think the term "mutable aliasing" and "what you want" don't 
work together. "mutable aliasing" is not about context of usage 
or what you want, it is a compile time attribute of a struct. I 
want to use associative arrays in composition because I think 
they are the way I view and use my data. `struct T { 
string[string] i; }` has mutable aliasing, like it or not. So, it 
is not so much that I want mutable aliasing - in fact I fear it. 
But once you use AAs it is a fact of life.

>
>> If you have had more success with a immutable with types 
>> containing
>> mutable aliasing and can share your success story that would be
>> great.
> [...]
>
> Maybe it's helpful to understand how D's const system works. The
> following diagram may help (please excuse the ASCII graphics):
>
> 	       const
> 	      /     \
> 	mutable     immutable
>
> What this means is that const subsumes mutable and immutable. A 
> mutable
> type can be implicitly converted to a const type (the receiver 
> of the
> const can't modify the data, which is fine since the code 
> holding the
> mutable reference can still mutate it), and so can immutable 
> (immutable
> cannot be modified, ever, and const doesn't let you modify it 
> either, so
> it's OK to make a const reference to immutable data). However, 
> you
> cannot implicitly convert between mutable and immutable, unless 
> you're
> copying the data by value.
>

yes - it requires transitive deep copy.

> So if you have immutable data and want to make changes, you 
> have to
> first make a copy of the data, then mutate it at will.
>
> What's the use of immutable, you ask? Immutable makes hard 
> guarantees
> about the non-changeability of some piece of data. This makes 
> it useful
> for implementing strings -- in fact, the 'string' type in D is 
> just an
> alias for immutable(char)[]. You can take substrings (slices) 
> of any
> given string freely, and be assured that your copy of the 
> (sub)string
> will never unexpectedly change its value from somewhere else in 
> the
> code. This saves the need for a lot of copying, which can be 
> costly.
>

I agree that string behaves as you say. I don't quite agree that 
immutable alone is the reason it does. I think it is an byproduct 
of the way immutable(T)[] is implemented. It is an implementation 
detail and relying on that could lead to bad deduction. We 
covered that here in this thread:
http://forum.dlang.org/post/jfjudswamyxlttgsdwva@forum.dlang.org


> One interesting subtlety here is that 'string' is 
> immutable(char)[], but
> not immutable(char[]). The latter would mean that the string 
> itself can
> never be changed -- you couldn't assign to it, you couldn't 
> append to
> it, etc., which would make strings a lot less useful than they 
> are. But
> by making strings a *mutable* array of *immutable* chars, you 
> allow the
> string to be appended to, substring'd, etc., all while 
> guaranteeing that
> the underlying bytes themselves will never change. So you can 
> have the
> best of both worlds: you can append to strings, take 
> substrings, assign
> strings to each other, etc., yet at the same time be assured 
> that the
> list of intermediate substrings you stored somewhere during the 
> process
> will continue to retain the values you assigned to them, 
> because the
> underlying bytes they point to are immutable, and therefore 
> guaranteed
> never to change.
>
>
> T

Agreed with the description of the behavior. But disagree on why. 
It works that way because T[] is modeled as contiguous memory and 
the api associated with slice of type immutable(T)[] means there 
is no unsafe sharing. So, `struct T { string[string] i; }` and 
`struct T { immutable(S)[string] }` do not have the same 
properties because they have a different layout model.

Is the suggestion here: use immutable in any composition context 
where you have slices and you want to not "worry about mutable 
aliasing". So, do like string does: any case where you have T[] 
as a member, prefer immutable(T)[] since then you don't have to 
worry about sharing. Well, not sure that works in general because 
the T in string is char and has no mutable aliasing itself. 
Suppose T itself has mutable aliasing, then what? Or, suppose it 
is a struct with no mutable aliasing *now*. Who's to say it won't 
change.

So, where does all this leave us w.r.t. a good set of guidelines?


More information about the Digitalmars-d-learn mailing list