July 15, 2016
On Friday, 15 July 2016 at 10:25:16 UTC, Shachar Shemesh wrote:
>
> I think the one that hurts the most is fixing "C++ fault" #3. It means there are many scenarios in which I could put const in C++, and I simply can't in D, because something somewhere needs to be mutable.

Then it is not const and marking it as const is a bug. D enforces to not write a bug, what's wrong with that?

July 15, 2016
On Friday, 15 July 2016 at 11:09:24 UTC, Patrick Schluter wrote:
> On Friday, 15 July 2016 at 10:25:16 UTC, Shachar Shemesh wrote:
>>
>> I think the one that hurts the most is fixing "C++ fault" #3. It means there are many scenarios in which I could put const in C++, and I simply can't in D, because something somewhere needs to be mutable.
>
> Then it is not const and marking it as const is a bug. D enforces to not write a bug, what's wrong with that?

One example is if you make a class that has an internal cache of something. Updating or invalidating that cache has no logical effect on the externally-observable state of the class. So you should be able to modify the cache even on a 'const' object. This is not a bug and I've seen it have a huge effect on performance - probably a lot more than the const optimizations Walter is talking about here.
July 15, 2016
On 07/14/2016 12:17 PM, Jesse Phillips wrote:
> On Tuesday, 12 July 2016 at 05:15:09 UTC, Shachar Shemesh wrote:
>> C++ fully defines when it is okay to cast away constness, gives you
>> aids so that you know that that's what you are doing, and nothing
>> else, and gives you a method by which you can do it without a cast if
>> the circumstances support it.
>>
>> D says any such cast is UB.
>>
>> Shachar
>
> Yeah C++ defines how you can modify const data after saying you can
> never modify data from a const qualified access path. §7.1.​6.1/3[1]
>
> I still haven't found someone who can explain how C++ can define the
> behavior of modifying a variable after casting away const. Sure it says
> that if the original object was mutable (not stored in ROM) than you can
> modify it, but that is true of D as well, but the language doesn't know
> the object is not stored in ROM so it can't tell you what it will do
> when you try to modify it, only you can.
>
> 1. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf

Getting back to D, a more appropriate definition that gives us enough flexibility to implement allocators etc. has to take time into account. Something like the following:

"During and after mutating a memory location typed as (unqualified) type T, no thread in the program (including the current thread) is allowed to effect a read of the same location typed as shared(T) or immutable(T)."

This allows us to implement portably allocators that mutate formerly immutable data during deallocation.


Andrei

July 15, 2016
On 07/15/2016 05:43 PM, Andrew Godfrey wrote:
> On Friday, 15 July 2016 at 11:09:24 UTC, Patrick Schluter wrote:
>> On Friday, 15 July 2016 at 10:25:16 UTC, Shachar Shemesh wrote:
>>>
>>> I think the one that hurts the most is fixing "C++ fault" #3. It means there are many scenarios in which I could put const in C++, and I simply can't in D, because something somewhere needs to be mutable.
>>
>> Then it is not const and marking it as const is a bug. D enforces to not write a bug, what's wrong with that?
> 
> One example is if you make a class that has an internal cache of something. Updating or invalidating that cache has no logical effect on the externally-observable state of the class. So you should be able to modify the cache even on a 'const' object. This is not a bug and I've seen it have a huge effect on performance - probably a lot more than the const optimizations Walter is talking about here.

Yes and the fact that D prohibits this incredibly common C++ design anti-pattern makes me very grateful about such choice. Logical const is terrible - either don't mark such objects as const or make cache separate.
July 15, 2016
On Friday, 15 July 2016 at 15:35:37 UTC, Dicebot wrote:

>> One example is if you make a class that has an internal cache of something. Updating or invalidating that cache has no logical effect on the externally-observable state of the class. So you should be able to modify the cache even on a 'const' object. This is not a bug and I've seen it have a huge effect on performance - probably a lot more than the const optimizations Walter is talking about here.
>
> Yes and the fact that D prohibits this incredibly common C++ design anti-pattern makes me very grateful about such choice. Logical const is terrible - either don't mark such objects as const or make cache separate.

+1

Use an interface that prevents external modifications, e.g. getters, but no setters.
July 15, 2016
On Friday, 15 July 2016 at 14:45:41 UTC, Andrei Alexandrescu wrote:
> On 07/14/2016 12:17 PM, Jesse Phillips wrote:
>> On Tuesday, 12 July 2016 at 05:15:09 UTC, Shachar Shemesh wrote:
>>> C++ fully defines when it is okay to cast away constness, gives you
>>> aids so that you know that that's what you are doing, and nothing
>>> else, and gives you a method by which you can do it without a cast if
>>> the circumstances support it.
>>>
>>> D says any such cast is UB.
>>>
>>> Shachar
>>
>> Yeah C++ defines how you can modify const data after saying you can
>> never modify data from a const qualified access path. §7.1.​6.1/3[1]
>>
>> I still haven't found someone who can explain how C++ can define the
>> behavior of modifying a variable after casting away const. Sure it says
>> that if the original object was mutable (not stored in ROM) than you can
>> modify it, but that is true of D as well, but the language doesn't know
>> the object is not stored in ROM so it can't tell you what it will do
>> when you try to modify it, only you can.
>>
>> 1. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
>
> Getting back to D, a more appropriate definition that gives us enough flexibility to implement allocators etc. has to take time into account. Something like the following:
>
> "During and after mutating a memory location typed as (unqualified) type T, no thread in the program (including the current thread) is allowed to effect a read of the same location typed as shared(T) or immutable(T)."
>
> This allows us to implement portably allocators that mutate formerly immutable data during deallocation.
>
>
> Andrei

Read or write.

For const(T) , same thing, but limited to write.

Everything else is UB, as it is already UB at the hardware level.
July 15, 2016
On Friday, 15 July 2016 at 14:43:35 UTC, Andrew Godfrey wrote:
> On Friday, 15 July 2016 at 11:09:24 UTC, Patrick Schluter wrote:
>> On Friday, 15 July 2016 at 10:25:16 UTC, Shachar Shemesh wrote:
>>>
>>> I think the one that hurts the most is fixing "C++ fault" #3. It means there are many scenarios in which I could put const in C++, and I simply can't in D, because something somewhere needs to be mutable.
>>
>> Then it is not const and marking it as const is a bug. D enforces to not write a bug, what's wrong with that?
>
> One example is if you make a class that has an internal cache of something. Updating or invalidating that cache has no logical effect on the externally-observable state of the class. So you should be able to modify the cache even on a 'const' object. This is not a bug and I've seen it have a huge effect on performance - probably a lot more than the const optimizations Walter is talking about here.

That's actually not true. Memory barrier needs to be emitted, and considered in the caller code.

July 15, 2016
On 07/15/2016 01:27 PM, deadalnix wrote:
> On Friday, 15 July 2016 at 14:45:41 UTC, Andrei Alexandrescu wrote:
>> On 07/14/2016 12:17 PM, Jesse Phillips wrote:
>>> On Tuesday, 12 July 2016 at 05:15:09 UTC, Shachar Shemesh wrote:
>>>> C++ fully defines when it is okay to cast away constness, gives you
>>>> aids so that you know that that's what you are doing, and nothing
>>>> else, and gives you a method by which you can do it without a cast if
>>>> the circumstances support it.
>>>>
>>>> D says any such cast is UB.
>>>>
>>>> Shachar
>>>
>>> Yeah C++ defines how you can modify const data after saying you can
>>> never modify data from a const qualified access path. §7.1.​6.1/3[1]
>>>
>>> I still haven't found someone who can explain how C++ can define the
>>> behavior of modifying a variable after casting away const. Sure it says
>>> that if the original object was mutable (not stored in ROM) than you can
>>> modify it, but that is true of D as well, but the language doesn't know
>>> the object is not stored in ROM so it can't tell you what it will do
>>> when you try to modify it, only you can.
>>>
>>> 1. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
>>
>> Getting back to D, a more appropriate definition that gives us enough
>> flexibility to implement allocators etc. has to take time into
>> account. Something like the following:
>>
>> "During and after mutating a memory location typed as (unqualified)
>> type T, no thread in the program (including the current thread) is
>> allowed to effect a read of the same location typed as shared(T) or
>> immutable(T)."
>>
>> This allows us to implement portably allocators that mutate formerly
>> immutable data during deallocation.
>>
>>
>> Andrei
>
> Read or write.
>
> For const(T) , same thing, but limited to write.

Thanks. Reworked:

"During and after mutating a memory location typed as (unqualified) type T, no thread in the program (including the current thread) is allowed to (a) effect a read of the same location typed as const(T) or immutable(T), or (b) effect a read or write of the same location typed as shared(T)."


Andrei


July 15, 2016
On 7/15/2016 3:25 AM, Shachar Shemesh wrote:
> On 15/07/16 13:13, Walter Bright wrote:
>
>> 1. no protection against casting away const and changing it anyway
>> 2. no protection against adding 'mutable' members and changing it anyway
>> 3. only good for one level, no way to specify a data structure of
>> generic type T as const
>>
>>> (and, sadly, not something D does very well)
>>
>> Explain. D fixes C++ faults 1..3.
>>
>
> Yes, it does. And the result is that const is well defined, safe, and completely
> impractical to turn on. There are many many places I'd have the compiler enforce
> const correctness in C++, where in D I just gave up. In one of those places we
> even went as far as to add run time checks that no one inadvertently changed a
> buffer.

When we first introduced const to D, this was a common complaint. People were accustomed to the weaknesses of C++ const and misinterpreted it as a strength :-) But over time, D const won most over as being a better way, because it offered guarantees that C++ const simply does not.

For one, it enables function purity.


> I think the one that hurts the most is fixing "C++ fault" #3. It means there are
> many scenarios in which I could put const in C++, and I simply can't in D,
> because something somewhere needs to be mutable.

That's the same argument that appeared when we introduced transitive const.
But it means you can't do FP in C++. It means const doesn't work with generic types and generic algorithms. It means that const in a function signature tells you little to nothing unless it is applied to basic types.


> Check this thread out to see that we actually do need something like #1 in D
> (though, at least if my suggestion is accepted, without throwing away the
> optimizations it allows).

Casting away const is only allowed in @system code. I agree that we need an improved definition of what happens when const is cast away in @system code, but in no case does it make things worse than in C++.


>>> In terms of optimizations, there are, indeed, cases where, had const
>>> not been
>>> removable, things could be optimized more. I don't think D has a right to
>>> complain about C++ in that regard, however.
>> Of course D does. I had to disable const optimizations in my C++
>> compiler, which is one of the motivations for the way const works in D.
> For const, yes. In almost every other aspect of the language, however, D favors
> safety over performance.
> Just look at range checks, memory allocation, default
> values, and those are just the examples off the top of my head.

1. range checks - can be disabled by a compiler switch
2. memory allocation - D programmers can use any of C++'s allocation methods
3. default values - are removed by standard dead assignment optimizations, or can be disabled by initializing with '= void;'

There is one opportunity for C++ that D eschews: taking advantage of undefined behavior on signed integer overflow to improve loops:

  http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html

Practically speaking, optimizers are heavily built for and tuned for C++ semantics. Opportunities that arise due to the semantics of D are not exploited, but this isn't the fault of the core language and does not make C++ better.

Opportunities for D that are not available in C++:

1. making use of const
2. making use of immutable
3. making use of function purity
4. making use of asserts to provide information to the optimizer


> I'm not saying that as a bad thing about D. It is a perfectly valid and
> reasonable trade off to make. I'm just saying D has no right to criticize C++
> for missed optimizations. People who live in glass houses should not throw stones.

I think your argument there is completely destroyed :-)

July 15, 2016
On 7/15/2016 7:43 AM, Andrew Godfrey wrote:
> One example is if you make a class that has an internal cache of something.
> Updating or invalidating that cache has no logical effect on the
> externally-observable state of the class. So you should be able to modify the
> cache even on a 'const' object.

Yes, that's the "logical const" argument.

The trouble with it is there's no way for the compiler to detect that's what you're doing, nor can it do any checks on it. In effect, C++ const becomes little more than a documentation suggestion.


> This is not a bug and I've seen it have a huge
> effect on performance - probably a lot more than the const optimizations Walter
> is talking about here.

You can do logical const in D just like in C++, and get those performance gains. You just can't call it "const". But you can call it /*logical_const*/ and get the same result.