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