Interested in D, spec confuses me.

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Tue Feb 2 15:41:07 PST 2016


On Tue, Feb 02, 2016 at 09:35:13PM +0000, IceCreamEater via Digitalmars-d wrote:
> On Tuesday, 2 February 2016 at 20:17:20 UTC, H. S. Teoh wrote:
> >On Tue, Feb 02, 2016 at 09:13:41PM +0100, anonymous via Digitalmars-d
> >wrote: [...]
> >>The other signature is no different. Two occurrences of "immutable",
> >>applying to two different things.
> >>
> >>I agree that it can be unclear to newbies what exactly is immutable
> >>when a method is marked immutable, but the meaning of the keyword is
> >>the same as elsewhere. Using another word there would be more
> >>confusing.
> >
> >Another way to think about it, is that the "immutable" on the
> >function means that the implicit `this` reference to the object is
> >immutable.
> >
> >
> >T
> 
> I thought immutable was added to the language as a better guarantee to
> 'const'. Which really tells me const wasn't cutting it and wasn't a
> proper guarantee.

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.

As an added benefit, you can also call func2 with immutable Data: you
know it's safe, because even though func2 doesn't require immutability,
it also guarantees that it won't touch the data. So you don't need to
write two copies of func2, one to work with mutable data and one to work
with immutable data. This is why both mutable and immutable can
implicitly cast to const.


T

-- 
He who does not appreciate the beauty of language is not worthy to bemoan its flaws.


More information about the Digitalmars-d mailing list