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/[email protected]


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?

Reply via email to