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