August 17, 2012
Well, since I'm not describing how const works anymore (although, it's still different than C++ due to the mutable keyword in C++, but I digress), I'll go ahead and jump in for this one...

On Friday, 17 August 2012 at 02:30:45 UTC, Mehrdad wrote:
> So unless you're expecting the compiler to have the implementation for the entire class available in order for it to be able to do any kind of optimization (in which case, it would have to do a whole bunch of inference to figure out the aliasing issues, which would amount to what a C++ could try do just as well), I'm not seeing where the additional guarantees/potential optimizations are.

I'll tackle the guarantees part and what the compiler could do. Combine const and pure and here you go:

int global;

struct MyStruct {
    int* y;

    //this() { } // Stop doing this, it doesn't compile. :P
    this(int* z) { y = z; }

    auto getValue() const pure {
        //++global; // error: cannot access mutable static data 'global'
        return this.y;
    }

    void impureThing() const {
       ++global;
    }
}

void func(ref const(MyStruct) s) pure {
   //... can only call pure functions
   // s.impureThing(); // error
}

import std.stdio;
void main() {
    auto s = MyStruct(&global);

    writeln(*s.getValue());
    func(s); // func is pure and s will be const ... thus,
    writeln(*s.getValue()); // guaranteed to be the same as first call
}



And this is different than C++. If C++ did have a "pure" keyword and it worked the same as D's pure keyword, then you still couldn't make the same inference all the time because C++ allows const things to internally mutate due to the mutable keyword.

Yeah, you have to start combining features to get the most out of it, but const + pure allows for more optimizations/reasoning than const or pure alone.
August 17, 2012
On Friday, 17 August 2012 at 03:36:28 UTC, Chris Cain wrote:
> Combine const and pure


Yes, I 100% realize 'pure' and 'immutable' are advantages over C++.
The question was about 'const' by itself, though, because otherwise that's not a fair comparison. (The goal is comparing C++ const to D const, not C++ const to D const + immutable + purity.)
August 17, 2012
On Friday, 17 August 2012 at 03:42:23 UTC, Mehrdad wrote:
> On Friday, 17 August 2012 at 03:36:28 UTC, Chris Cain wrote:
>> Combine const and pure
>
>
> Yes, I 100% realize 'pure' and 'immutable' are advantages over C++.
> The question was about 'const' by itself, though, because otherwise that's not a fair comparison. (The goal is comparing C++ const to D const, not C++ const to D const + immutable + purity.)

To clarify... the motivation for this question in the first place was the fact that I've been consistently told (and have read) that D const provides more guarantees than C++ const, so I was trying to figure out how.


If what you're saying is that the extra guarantees only translate into optimizations when used along with immutable/pure, then that's a completely valid answer. :) Is that what you're saying?

(i.e. I DO realize that combining them would give you an optimization, but the question is -- must you combine const with something else to gain an advantage over C++?)
August 17, 2012
On Friday, 17 August 2012 at 03:42:23 UTC, Mehrdad wrote:
> Yes, I 100% realize 'pure' and 'immutable' are advantages over C++.
> The question was about 'const' by itself, though, because otherwise that's not a fair comparison. (The goal is comparing C++ const to D const, not C++ const to D const + immutable + purity.)

I don't know what to tell you then. If you have a non-const pure function and add const to it, then you can make all kinds of optimizations for free basically. But, as you've found, if you completely ignore purity, you can find some ways around that and the compiler would have to potentially perform some expensive checks to do the same optimizations (unless they're just not valid).

You can probably do more optimizations on primitive types easier (but they obviously have their own limitations), but many of those same optimizations could potentially be done on C++'s const primitives as well. But really, the biggest differentiation between C++'s const and D's const comes in to play with non-primitives. C++'s const on classes, for instance, gives you _no_ guarantees (none, at all, unless you dive into the class and take a close look) and therefore _no_ optimizations without major costly calculations and certainly no easy reasoning.

OTOH, D's const does give you guarantees. Combine that with pure and you've got huge amounts of reasoning. And yes, I'd say this answers your topic's question on what D's const does for you. Clearly, a pure function without const doesn't provide you with the same reasoning as a pure function with const.

writeln(s.pureMutatingFunc());
writeln(s.pureMutatingFunc()); // guaranteed not to change globals ... restricted to only changing s and, transitively, everything s points to

const: Prevents changing s and, transitively, everything s points to (through the s view, of course ... doesn't say anything about global data or other views available to you because of global data, as you've found).

ergo, we can do this:

writeln(s.pureConstFunc());
writeln(s.pureConstFunc()); // definitely the same as above call


Now an impure function can modify globals, so it shouldn't surprise you that if s has a pointer to a global, you can modify the global and therefor the view s represents will also have a change.
August 17, 2012
On Friday, 17 August 2012 at 03:44:38 UTC, Mehrdad wrote:
> To clarify... the motivation for this question in the first place was the fact that I've been consistently told (and have read) that D const provides more guarantees than C++ const, so I was trying to figure out how.
>
>
> If what you're saying is that the extra guarantees only translate into optimizations when used along with immutable/pure, then that's a completely valid answer. :) Is that what you're saying?
>
> (i.e. I DO realize that combining them would give you an optimization, but the question is -- must you combine const with something else to gain an advantage over C++?)

Well, I guess if you're modifying globals and you're going to have your object pointing to those globals, then no you aren't going to get any optimizations that way. And code like that will be harder to reason about as well. I'd have a hard time saying that the "extra guarantees only translate into optimizations when used along with immutable/pure". I'd say in more realistic code where you don't do absurd things like intentionally put a global into a const variable just to mutate the global willy nilly, you'd get optimizations (although, it'd probably have to check for those absurd cases as well, but non-trivial optimizations are still optimizations and it's possible to do).

It is something that would be much more difficult to do with C++ ... so much so, that if C++ didn't have the const keyword, I'd imagine the problem would still be roughly the same difficulty. Not only does C++ have to check globals like D would, but it has to check to make sure none of the mutable-marked member variables change, and if they do, then it would have to check to make sure that the change wouldn't result in changes in other functions (even if they're const functions, btw) along the way and ... etc.

But yeah, you do get guarantees with D's const that you don't with C++ const (mutable keyword, again, which makes C++'s const useless in all non-trivial cases).

I will say that D's features have a synergy about them and combining them with another one will make them more powerful than the two individually. So const + pure is a pretty good combination. That does make many optimizations (that would normally be difficult) completely trivial to do.
August 17, 2012
On Friday, August 17, 2012 05:11:49 Mehrdad wrote:
> On Friday, 17 August 2012 at 02:49:45 UTC, Jonathan M Davis wrote:
> > But take this code for example:
> > 
> > auto i = new int;
> > *i = 5;
> > const c = i;
> > writeln(c);
> > func(c); //obviously takes const or it wouldn't compile
> > writeln(c);
> > 
> > The compiler _knows_ that c is the same before and after the call to func, because it knows that no other references to that data can exist.
> 
> Is there any reason why your example didn't just say
> 
> > const(int*) c = null;
> > writeln(c);
> > func(c);
> > writeln(c);
> 
> i.e. What was the point of 'i' there?
> And why can't a C++ compiler do the same thing?
> 'c' is a const object, so if C++ code was to modify it, it would
> be undefined behavior, just like in D.
> Sorry, I'm a little confused at what you were illustrating here.

1. Because it wasn't creating as const, C++ could legally mutate the object in func by casting away const (meaning that it can't assume that c is unchanged after the call to func), which is not the case in D. But if it were created as const, then it would undefined behavior in both languages.

2. If you want to assign an actual value to c rather than null, you either need to use a helper function or create a mutable one first, because there's no do something like

const int* c = new int(5);

and have c point to an int with value 5.

So, with my example, the D code can guarantee that func doesn't modify either c or what's pointed to by c, whereas C++ provides no such guarantee. So, any optimizations which could be done based on the fact that func didn't change C's value can be done in D but not C++.

- Jonathan M Davis
August 17, 2012
On Friday, 17 August 2012 at 03:57:21 UTC, Chris Cain wrote:
> On Friday, 17 August 2012 at 03:42:23 UTC, Mehrdad wrote:
>> Yes, I 100% realize 'pure' and 'immutable' are advantages over C++.
>> The question was about 'const' by itself, though, because otherwise that's not a fair comparison. (The goal is comparing C++ const to D const, not C++ const to D const + immutable + purity.)
>
> I don't know what to tell you then. If you have a non-const pure function and add const to it, then you can make all kinds of optimizations for free basically. But, as you've found, if you completely ignore purity, you can find some ways around that and the compiler would have to potentially perform some expensive checks to do the same optimizations (unless they're just not valid).
>
> You can probably do more optimizations on primitive types easier (but they obviously have their own limitations), but many of those same optimizations could potentially be done on C++'s const primitives as well. But really, the biggest differentiation between C++'s const and D's const comes in to play with non-primitives. C++'s const on classes, for instance, gives you _no_ guarantees (none, at all, unless you dive into the class and take a close look) and therefore _no_ optimizations without major costly calculations and certainly no easy reasoning.
>
> OTOH, D's const does give you guarantees. Combine that with pure and you've got huge amounts of reasoning. And yes, I'd say this answers your topic's question on what D's const does for you. Clearly, a pure function without const doesn't provide you with the same reasoning as a pure function with const.
>
> writeln(s.pureMutatingFunc());
> writeln(s.pureMutatingFunc()); // guaranteed not to change globals ... restricted to only changing s and, transitively, everything s points to
>
> const: Prevents changing s and, transitively, everything s points to (through the s view, of course ... doesn't say anything about global data or other views available to you because of global data, as you've found).
>
> ergo, we can do this:
>
> writeln(s.pureConstFunc());
> writeln(s.pureConstFunc()); // definitely the same as above call
>
>
> Now an impure function can modify globals, so it shouldn't surprise you that if s has a pointer to a global, you can modify the global and therefor the view s represents will also have a change.



Okay so basically, the conclusion I'm drawing is that you have to combine it with pure/immutable in order to get much more out of it than compared with C++; otherwise, it's not really different.

Thanks!
August 17, 2012
On Friday, August 17, 2012 04:46:34 Chris Cain wrote:
> Notice that I'm making an immutable(S) in that example.

I missed that. It's a known bug and in bugzilla somewhere. I'd have to go digging to find the exact bug# though. const constructors have the same problem.

- Jonathan M Davis
August 17, 2012
On Friday, 17 August 2012 at 04:17:05 UTC, Jonathan M Davis wrote:
> On Friday, August 17, 2012 05:11:49 Mehrdad wrote:
>> On Friday, 17 August 2012 at 02:49:45 UTC, Jonathan M Davis wrote:
>> > But take this code for example:
>> > 
>> > auto i = new int;
>> > *i = 5;
>> > const c = i;
>> > writeln(c);
>> > func(c); //obviously takes const or it wouldn't compile
>> > writeln(c);
>> > 
>> > The compiler _knows_ that c is the same before and after the
>> > call to func, because it knows that no other references to that
>> > data can exist.
>> 
>> Is there any reason why your example didn't just say
>> 
>> > const(int*) c = null;
>> > writeln(c);
>> > func(c);
>> > writeln(c);
>> 
>> i.e. What was the point of 'i' there?
>> And why can't a C++ compiler do the same thing?
>> 'c' is a const object, so if C++ code was to modify it, it would
>> be undefined behavior, just like in D.
>> Sorry, I'm a little confused at what you were illustrating here.
>
> 1. Because it wasn't creating as const, C++ could legally mutate the object in
> func by casting away const (meaning that it can't assume that c is unchanged
> after the call to func), which is not the case in D. But if it were created as
> const, then it would undefined behavior in both languages.
>
> 2. If you want to assign an actual value to c rather than null, you either
> need to use a helper function or create a mutable one first, because there's no
> do something like
>
> const int* c = new int(5);
>
> and have c point to an int with value 5.
>
> So, with my example, the D code can guarantee that func doesn't modify either
> c or what's pointed to by c, whereas C++ provides no such guarantee. So, any
> optimizations which could be done based on the fact that func didn't change
> C's value can be done in D but not C++.
>
> - Jonathan M Davis


Oh, so you're talking about the value of 'i' /after/ all that code has executed... I kinda got lost in there because you didn't seem to use 'i' afterwards.

Interesting, that's a great example I think. It might be worth putting on the website somewhere, especially because it clearly shows that the compiler doesn't need the source of func to infer that 'i' won't be changed.


Cool, thanks a bunch, as always! :)

August 17, 2012
On Friday, 17 August 2012 at 04:17:33 UTC, Mehrdad wrote:
> Okay so basically, the conclusion I'm drawing is that you have to combine it with pure/immutable in order to get much more out of it than compared with C++; otherwise, it's not really different.
>
> Thanks!

You posted this before seeing the additional clarifications Mr. Davis and I posted, and I understand that, but I just wanted to say that factoring what we both said I think it's clear that it is really different. Not bulletproof (I'd say const + pure would be nearly bulletproof and immutable + pure would truly be bulletproof if you started considering multi-threading), but certainly better than C++.