On Tuesday, 2 February 2016 at 23:41:07 UTC, H. S. Teoh wrote:
You're misunderstanding D's type system. Immutable is not a "better const", as though const is somehow defective. Perhaps the following diagram may help to clear things up:

                const
               /     \
        (mutable)   immutable

What this means is that both mutable and immutable are implicitly convertible to const. Or, to put it another way, const is a kind of "wildcard" that can point to underlying data that's either mutable or immutable.

Mutable data is, well, mutable -- anybody who can get to it, can modify it. Immutable means *nobody* can modify it once it's initialized.

Why const, then? Const is useful for when a function doesn't care whether the underlying data is mutable or not, because it doesn't need to change the data. Const provides guarantees to the caller that the function won't touch the data -- even if the data is actually mutable in the caller's context. It's a "no-mutation view" on data that's possibly mutable by a third party.

Furthermore, since const provides actual guarantees that the called function isn't going to touch the data, this opens up optimization opportunities for the compiler. E.g., it can assume that any part(s) of the data that are held in registers will remain valid after the function call (provided said registers aren't touched by the function), so it doesn't need to issue another load after the function returns. As a contrived example, say you have code like this:

        struct Data { int x; }
        int func1(Data* d) { ... }
        int func2(const(Data)* d) { ... }
        ...
        void main() {
                Data d;
                int value1 = d.x*func1(&d) + d.x;
                int value2 = d.x*func2(&d) + d.x;
                d.x++;
        }

When evaluating value1, the compiler may have issued a load for the first occurrence of d.x, then it calls func1. But since func1 may modify d.x, the second d.x needs another load in order to ensure the correct value is used.

When evaluating value2, however, since func2 takes a const pointer to the Data, the compiler knows the value of d.x cannot possibly change across that function call, so it can safely reuse the value of d.x that was previously loaded. It may also go further and refactor the expression as d.x*(func2(&d) + 1), because the const guarantees that func2 can't mutate d.x behind our backs and invalidate the result. Such a refactoring is invalid with func1, because there is no guarantee that d.x will have the same value after func1 returns.

Now, the same argument applies if immutable was used in place of const. However, the last line in main() illustrates why we need const rather than immutable in this case: we actually *want* to modify d.x in main(). We just don't want func2 to touch anything. So we can't use immutable -- since immutable means *nobody* can touch the data. So, const provides both the guarantee that func2 won't touch the data, thus allowing the aforementioned optimization, and also continues to let main() mutate the data at its leisure.


T

In C90+, const can apply to both values and pointers to those values. And since pointers are themselves values of a memory address that is consistent usage. That seems to be the only meaningful distinction here. Pointing to a const value makes the pointer mutable but the value immutable. Pointing a const to a value makes the pointer immutable but the value mutable. etc.

Immutable accomplishes nothing distinct here that I can see, other than making the use of const confusing. To make a function not change a value you declare a const input. Because it is the value declared in the function definition that is a const, not the value you pass it. "Passing as const" doesn't make any logical sense. To be honest, it smells like the kind of opaque cleverness D is ostensibly supposed to obviate.

Reply via email to