View mode: basic / threaded / horizontal-split · Log in · Help
July 12, 2012
immutability and constness
I'm fairly new to D, but I have been using C++ for a while now 
(about 5 years, selftaught).

From what I have learned, const in C++ is inconsistent.

For example:

int main()
{
   const int x = 0; // could be placed in ROM because it's known 
at compile time
}

void f(int x)
{
  const int y = x * x; // logical const
  // blah blah blah
}

const can be also cast away (const_cast if I remember correctly - 
I wasn't doing it often :) ) and mess everything.

From what I understand, immutable in D is truly immutable. But I 
have seen some code here on the forum that casts aways 
immutability... (Is that even defined behaviour? What if that 
thing was in ROM?)

D has const as well... This is were it becomes a bit tricky for 
me. To be honest, I haven't got the book about D - it should(does 
it?) have information about that.

Can someone explain what are D's goal about immutable, const - 
and why it is bitwise const and not logical? How does it benefit 
programs? I have read something about that in the "general 
discussions" forum but posted the question here because it's for 
me (and others) to learn about that.

Thanks.
July 12, 2012
Re: immutability and constness
On Thu, Jul 12, 2012 at 02:14:30AM +0200, Minas Mina wrote:
[...]
> D has const as well... This is were it becomes a bit tricky for me.
> To be honest, I haven't got the book about D - it should(does it?)
> have information about that.

You should get the book, it explains all the basic concepts (and some
advanced ones too).

Basically the way it works is like this: mutable (unqualified) and
immutable can both implicitly convert to const. So it's sorta like the
hierarchy:

	     const
	    /     \
	mutable  immutable

It's OK to convert immutable to const because const promises never to
touch the data. It's also OK to convert mutable to const, because, well,
not changing mutable data is OK too.

In some cases, you can convert immutable to mutable (if the object has
value semantics transitively to all its subparts), but as a rule, you
can't. Immutable is bit-wise unchangeable, no matter what.


> Can someone explain what are D's goal about immutable, const - and why
> it is bitwise const and not logical?

Mainly because logical const is not enforceable. Logical const means the
object does not _visibly_ change, but some hidden internal state may be
changing wildly. Visible, of course, w.r.t. to non-private code that
uses the object. But what constitutes a visible change? For example, if
I have a class:

	class C {
		int x, y;
		int getX() { return x; }
		void doStuff() {
			if (y++ > 100000) x++;
		}
	}

For the first 99999 times you call doStuff, no visible change is made,
but the next time you call it, a visible change happens. So it is not
logically const. But how would the compiler be able to tell? There could
be very complex code inside doStuff(), and the compiler would
essentially have to solve the halting problem (an unsolvable problem) in
order to know whether doStuff() is logically const.

So the only way logical const can work is to make it const "by
convention", that is, you write void doStuff() const{}, but it's up to
you the programmer to ensure that it's actually const, since the
compiler has no way to verify it. This is bad, because programmers are
human, and humans make mistakes. So the const qualifier becomes useless,
it's just an annotation, with no real guarantees.

The only way to make const enforceable is to make it bitwise const.


> How does it benefit programs? I have read something about that in the
> "general discussions" forum but posted the question here because it's
> for me (and others) to learn about that.
[...]

Immutable is a strong guarantee the data will never, ever change. So
it's safe to perform a whole range of optimizations, like allowing
multiple threads to read the data without any locks (since it doesn't
change, there is no chance of race conditions), common subexpression
elimination (reading the data multiple times across function calls is
the same as reading it once: the function call cannot change the data
because it's immutable). The compiler can put this data in ROM, where
the data physically can't change -- and will be able to verify there is
no attempt to change the data in the code.

The problem is, immutable is very restrictive, and hard to work with
(you can't change anything, you have to make a copy and change that).
So if you have some immutable data and some mutable data, you can't use
the same code to read them (because you can't convert between them).

So a common ground is introduced: const. Const means whoever has the
const object can't change anything, but somebody somewhere else may
change it. So mutable can convert to const, obviously, but so can
immutable (since in the case of immutable, _nobody_ can change it, so
the fact that the current code that handles the const data won't change
it either means that the data remains immutable). The advantage of this
is that now you can have the same code process both mutable and
immutable data. It's OK since you only ever read the data.


T

-- 
Spaghetti code may be tangly, but lasagna code is just cheesy.
July 12, 2012
Re: immutability and constness
On Thursday, 12 July 2012 at 00:37:22 UTC, H. S. Teoh wrote:
> On Thu, Jul 12, 2012 at 02:14:30AM +0200, Minas Mina wrote:
> [...]
>> ********** wall o' text **********

You forgot about their quantum uncertainty loving transvestite 
cousin: inout.
July 12, 2012
Re: immutability and constness
Thanks a lot!
So what is the problem with (logical)const? Is it needed that
much? And why some methods (toString(), toHash()) HAVE to be
const? I mean, what's the problem if they aren't?
July 12, 2012
Re: immutability and constness
On 07/12/2012 07:13 AM, Minas wrote:
> Thanks a lot!
> So what is the problem with (logical)const? Is it needed that
> much? And why some methods (toString(), toHash()) HAVE to be
> const? I mean, what's the problem if they aren't?

Here is the problem:

class C
{}

// Good foo: Takes as const(C) because it is not going to
// change the object
void foo(const(C) c)
{
    c.toString();
    /*
     * With dmd 2.059:
     *
     * Error: function object.Object.toString() is not
     * callable using argument types () const
     */
}

void main()
{}

Notes:

1) The current attempt of making toString() and friends const would fix 
that problem.

2) It brings a limitation: What job does the base class have 
constraining the derived class's toString()? What if the derived really 
needed to mutate the object in toString()?

(That is why I almost never make abstract member functions 'const' in my 
C++ coding. Base can't know mutability needs of the derived.)

3) [This warrants another thread or even an article.] You may argue that 
foo() API is problematic because despite the sensible const(C), which 
nicely binds to mutable and immutable, it cannot be forwarded 
efficiently to another function that takes an immutable:

class C
{}

void foo(const(C) c)
{
    bar(c);
    /* Error: function deneme.bar (immutable(C) c) is not
     * callable using argument types (const(C))
     */
}

void bar(immutable(C) c)
{}

void main()
{}

But what if the c that foo() took was immutable to begin with? foo()'s 
const(C) parameter erases that information.

To unerase, it must be a template and its parameter must be smart to 
copy if the original object was mutable:

// Warning: Very raw idea!
void foo(T)(CopyableOrForwardableConst!C c)
{
    bar(c);    // makes an immutable copy if necessary,
    zar(c);    // or not, depending on
               // whether original object c is mutable or immutable
}

Ali

-- 
D Programming Language Tutorial: http://ddili.org/ders/d.en/index.html
July 13, 2012
Re: immutability and constness
On Thursday, 12 July 2012 at 14:42:17 UTC, Ali Çehreli wrote:
> On 07/12/2012 07:13 AM, Minas wrote:
>> Thanks a lot!
>> So what is the problem with (logical)const? Is it needed that
>> much? And why some methods (toString(), toHash()) HAVE to be
>> const? I mean, what's the problem if they aren't?
>
> Here is the problem:
> ....

> (That is why I almost never make abstract member functions 
> 'const' in my C++ coding. Base can't know mutability needs of 
> the derived.)
> Ali

I see. Just because toString() in object is const, so must be in
the derived classes thus limiting them.

So now I understand the solution that Andrei is considering
(removing those from object). I think this is a good thing to do,
those functions don't belong there (I have had some experience
with Java's equals(Object o). All I say is that it sucks.)

In C++, I would use a templated function. If the type didn't
provide those methods it would be a compile error. Nice and clean
:)

Thanks for the reply :)
Top | Discussion index | About this forum | D home