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?