mutable, const, immutable guidelines

Daniel Davidson nospam at spam.com
Wed Oct 9 08:50:54 PDT 2013


On Wednesday, 9 October 2013 at 04:41:35 UTC, Ali Çehreli wrote:
> On 10/08/2013 03:03 PM, qznc wrote:
>
> > On Wednesday, 2 October 2013 at 13:09:34 UTC, Daniel Davidson
> wrote:
> >> 1. If a variable is never mutated, make it const, not
> immutable.
> >> 2. Make the parameter reference to immutable if that is how
> you will
> >> use it anyway. It is fine to ask a favor from the caller.
> >> ...
> >
> > I think guideline 1 should be about "arguments" not
> "variables".
> > Functions should take const arguments, so they can be called
> with
> > mutable and immutable values.
>
> As demonstrated in the presentation, const erases the actual 
> type. So, the function must take a copy if the argument is to 
> be used in an immutable context.
>
> Instead, if the immutable appears on the interface, that copy 
> will be avoided.
>

True. But what does it mean to be "used in an immutable context". 
In your example you pass a variable to a function `void 
usefulFunction(string s)`. The problem with transitivity is once 
you start making rules regarding it, those rules must also be 
transitive (or turtles all the way down as you say). The point 
is, the desire in the example wants to use the guideline only at 
the top level and break it at the next level. If usefulFunction 
were following the guideline it would have a signature `void 
usefulFunction(const(char)[] s)` instead.

If the guideline were: "if a function parameter is not mutated, 
make it const" and it were applied that way everywhere, the 
problem you suggest would not be hit. But we can't go changing 
any `standardFunction(string s)` to 
`standardFunction(const(char)[] s)`. Using const on parameters 
seems to be the prevalent guideline - even though passing strings 
breaks it.

In regards to parameters, when, if ever, is there benefit to 
immutable over const (if you assume all called functions from 
that function have const parameters/signatures). The only reason 
I can think is if a *member* function wants to hold onto a 
reference or copy for future use and the designer wants to 
guarantee that member is truly immutable. An example would be a 
struct that initializes with an immutable(T) that the struct 
wants to then use throughout its life.

struct S { this(ref immutable(T) t) { _t = t; }  immutable(T) _t; 
}

This is all very confusing and to me unsettled. I've brought up 
questions like this many times and have yet to hear Walter or 
Andrei weigh in. Either we (you and I) are missing something and 
making things much more difficult than necessary or it is just 
difficult. I think a third likely possibility is that composition 
in the face of structs with mutable aliasing is not heavily used 
by them.

> > I would rephrase the second guideline as: "Never dup or idup
> an argument
> > to make it mutable or immutable, but require the caller to do
> this
> > (might be able to avoid it)".
>
> Agreed. But it means you agree with me that immutable should 
> indeed appear on function signatures as needed.
>

I don't really agree. Naturally, if const were used all the way 
down there would be no need for copies. It is the introduction of 
immutable (via string in your example) that breaks the rule and 
causes the pain. But then if the rule were adopted globally - 
immutable is never used on parameters in a signature, only const 
is used, when would you start seeing any benefit to the immutable 
keyword?

> > For more guidelines:
> >
> > 3. Return value, which are freshly created, should never be
> const. They
> > should be mutable or immutable.
>
> Agreed. I say that it should be mutable if the function is 
> pure, as such a return values is automatically convertible to 
> immutable.
>
> > Basically, I cannot come up with a case, where const would be
> preferable
> > to mutable and immutable. Maybe someone is able to find a
> good example?
>

I don't know if these are good examples, but they are examples:

 From zip.d:
         const(void)[] compress(const(void)[] buf)

 From vibe:
  	const(Json) opIndex(string key) const {


[snip]

> > 4. Data structures should not restrict themselves to be
> mutable, const,
> > or immutable.
>
> What is the template of a struct that can be used as such? 
> Providing simple values seems to be insufficient:
>
> struct MyInt
> {
>     int i;
>     private int[] history;
> }
>
> What else should the struct above have to make it usable as 
> mutable, const, immutable?
>

I'm confused by the term "usable as mutable, const, immutable"? 
Isn't it true that types are not mutable, const, immutable in 
their definition but only in their context?

[snip]

> We have to nail this down already. We have this great addition 
> of immutable in the language but we either don't know how to 
> use it or the language is lacking hopefully trivial things to 
> make it work.
>

Amen, brother!

Either it is a great addition and usable, or it is not so great 
an addition. At times I find myself questioning even the basic 
benefit of immutable in the context of sharing. I've heard that 
immutable is great because it means the data can be shared across 
threads and you have confidence it will never change. This sounds 
good. But then you have to choose signatures:

void foo(const(MutableType) mt);
void foo(immutable(MutableType) mt);

Naturally the inclination is to choose the second as it is a 
stronger guarantee that no threads are changing the data. Cool. 
But wait, the first one still probably requires the same 
guarantee, even if it does not state it. If in the const case 
another thread changes the state of mt during the function foo 
then it will fail. foo actually requires that mt not be change by 
other threads during its operation - that is the only sane way of 
using the data in the function.

Take, for example, LinearCongruentialEngine from random.d. It has 
a function:

     bool opEquals(ref const LinearCongruentialEngine rhs) const

Why is it using const here instead of immutable? Does it not care 
about other threads? No - it just is not smart enough to deal 
with it. It assumes other threads won't be changing it or if they 
are caveat developer. So when do you use immutable as a signal 
that not only will this function not change it, no thread will 
change it either? Probably when you know you have to deal with 
threading. But that then would be coupling two maybe orthogonal 
decisions - the threading of a program and the signature of 
functions.

Where is Scott Myers when you need him :-)


> Ali


More information about the Digitalmars-d-learn mailing list