August 28, 2018
On Tuesday, 28 August 2018 at 17:53:36 UTC, H. S. Teoh wrote:
> On Tue, Aug 28, 2018 at 10:20:06AM -0700, Manu via
>> D has no way to express head-const, and it turns out it's a tremendously useful concept.
>
> I can live without head-const... but what *really* makes const painful for me is the lack of head-mutable. I.e., given a const container (which implies const objects), there is no universal way to obtain a mutable reference to said const objects, unless you tread into UB territory by forcefully casting it away.  This makes const so limited in applicability that, for the most part, I've given up using const at all, in spite of having tried quite hard to use it as much as possible for years.

Simen's opHeadMutable [0] was pretty good solution to this const range stuff, but for some reason (not specified by anyone in the thread) it didn't seem to catch on :/

[0] https://forum.dlang.org/post/zsaqtmvqmfkzhrmrmrju@forum.dlang.org


August 28, 2018
On Tuesday, 28 August 2018 at 17:02:46 UTC, H. S. Teoh wrote:
> On Tue, Aug 28, 2018 at 08:18:57AM +0000, Eugene Wissner via Digitalmars-d wrote: [...]
>> There are still valid use cases where const should be "broken". One of them is mutex (another one caching). I have very little experiance in multi-threaded programming, but what do you think about "mutable" members, despite the object is const?
>
> The problem with compromising const is that it would invalidate any guarantees const may have provided.  Const in D is not the same as const in languages like C++; const in D means *physical* const, as in, the data might reside in ROM where it's physically impossible to modify. Allowing the user to bypass this means UB if the data exists in ROM.

I feel that such a narrow use case, wouldn't you just use something like immutable instead.

> Plus, the whole point of const in D is that it is machine-verifiable, i.e., the compiler checks that the code does not break const in any way and therefore you are guaranteed (barring compiler bugs) that the data does not change.  If const were not machine-verifiable, it would be nothing more than programming by convention, since it would guarantee nothing.  Allowing const to be "broken" somewhere would mean it's no longer machine-verifiable (you need a human to verify whether the semantics are still correct).

This is still not true, it is not machine verifiable as it is. It can be bypassed quite easily, as a const object can be assigned from an non-const one. There's no way to offer that guarantee.

import std.format : format;

struct Type
{
    int value;
}

void test(const ref Type type, int* ptr)
{
    int first = type.value;

    *ptr = first + 1;

    assert(type.value == first, format!"%d != %d"(type.value, first));
}

void main()
{
    Type type = Type(10);
    test(type, &type.value);
}
August 28, 2018
On Tue, Aug 28, 2018 at 07:39:20PM +0000, tide via Digitalmars-d wrote:
> On Tuesday, 28 August 2018 at 17:02:46 UTC, H. S. Teoh wrote:
> > On Tue, Aug 28, 2018 at 08:18:57AM +0000, Eugene Wissner via Digitalmars-d wrote: [...]
> > > There are still valid use cases where const should be "broken". One of them is mutex (another one caching). I have very little experiance in multi-threaded programming, but what do you think about "mutable" members, despite the object is const?
> > 
> > The problem with compromising const is that it would invalidate any guarantees const may have provided.  Const in D is not the same as const in languages like C++; const in D means *physical* const, as in, the data might reside in ROM where it's physically impossible to modify.  Allowing the user to bypass this means UB if the data exists in ROM.
> 
> I feel that such a narrow use case, wouldn't you just use something like immutable instead.

The problem is that immutable implicitly converts to const.  Basically, const means "I guarantee I will never modify this data (though someone else might", and immutable means "nobody will ever modify this data". You cannot allow const to mutate without risking breakage with immutable.  If the original data came from a mutable reference, you can probably get away with casting const away. But if it came from an immutable object, casting const away is UB.  Allowing const to be "sometimes" modified is also UB.


> > Plus, the whole point of const in D is that it is machine-verifiable, i.e., the compiler checks that the code does not break const in any way and therefore you are guaranteed (barring compiler bugs) that the data does not change.  If const were not machine-verifiable, it would be nothing more than programming by convention, since it would guarantee nothing.  Allowing const to be "broken" somewhere would mean it's no longer machine-verifiable (you need a human to verify whether the semantics are still correct).
> 
> This is still not true, it is not machine verifiable as it is. It can be bypassed quite easily, as a const object can be assigned from an non-const one. There's no way to offer that guarantee.

You misunderstand. Const means "this code cannot modify this object no matter what".  It does not guarantee somebody else can't modify it (you want immutable for that).  Both mutable and immutable implicitly convert to const, therefore it is imperative that code that handles const never modifies the data, because you don't know the provenance of the data: it could have come from an immutable object.  Allowing const to "sometimes" modify stuff will violate immutable and cause UB.

Whether a piece of code modifies the data is certainly machine-verifiable -- but only if there are no backdoors to const. If there are, then the compiler cannot feasibly verify const, since it would need to transitively examine all code called by the code in question, but the source code may not be always available.

Even if the data came from a mutable object, it does not make it any less machine-verifiable, since what we're verifying is "this code does not modify this data", not "this data never changes".  For the latter, immutable provides that guarantee, not const.  It is possible, for example, to obtain a const reference to a mutable object, and have one thread modify the object (via the mutable reference) while another thread reads it (via the const reference).  You cannot guarantee that the data itself won't change, but you *can* guarantee that the code holding the const reference (without access to the mutable reference) isn't the one making the changes.


T

-- 
A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived. -- Michael B. Allen
August 28, 2018
On Tue, Aug 28, 2018 at 06:44:37PM +0000, aliak via Digitalmars-d wrote:
> On Tuesday, 28 August 2018 at 17:53:36 UTC, H. S. Teoh wrote:
> > On Tue, Aug 28, 2018 at 10:20:06AM -0700, Manu via
> > > D has no way to express head-const, and it turns out it's a tremendously useful concept.
> > 
> > I can live without head-const... but what *really* makes const painful for me is the lack of head-mutable. I.e., given a const container (which implies const objects), there is no universal way to obtain a mutable reference to said const objects, unless you tread into UB territory by forcefully casting it away.  This makes const so limited in applicability that, for the most part, I've given up using const at all, in spite of having tried quite hard to use it as much as possible for years.
> 
> Simen's opHeadMutable [0] was pretty good solution to this const range stuff, but for some reason (not specified by anyone in the thread) it didn't seem to catch on :/
> 
> [0] https://forum.dlang.org/post/zsaqtmvqmfkzhrmrmrju@forum.dlang.org
[...]

Probably because nobody pushed it hard enough to make it happen.


T

-- 
It only takes one twig to burn down a forest.
August 29, 2018
On Tuesday, 28 August 2018 at 20:32:29 UTC, H. S. Teoh wrote:
> On Tue, Aug 28, 2018 at 07:39:20PM +0000, tide via Digitalmars-d wrote:
>> On Tuesday, 28 August 2018 at 17:02:46 UTC, H. S. Teoh wrote:
>> > On Tue, Aug 28, 2018 at 08:18:57AM +0000, Eugene Wissner via Digitalmars-d wrote: [...]
>> > > There are still valid use cases where const should be "broken". One of them is mutex (another one caching). I have very little experiance in multi-threaded programming, but what do you think about "mutable" members, despite the object is const?
>> > 
>> > The problem with compromising const is that it would invalidate any guarantees const may have provided.  Const in D is not the same as const in languages like C++; const in D means *physical* const, as in, the data might reside in ROM where it's physically impossible to modify.  Allowing the user to bypass this means UB if the data exists in ROM.
>> 
>> I feel that such a narrow use case, wouldn't you just use something like immutable instead.
>
> The problem is that immutable implicitly converts to const.  Basically, const means "I guarantee I will never modify this data (though someone else might", and immutable means "nobody will ever modify this data". You cannot allow const to mutate without risking breakage with immutable.  If the original data came from a mutable reference, you can probably get away with casting const away. But if it came from an immutable object, casting const away is UB.  Allowing const to be "sometimes" modified is also UB.
>
>
>> > Plus, the whole point of const in D is that it is machine-verifiable, i.e., the compiler checks that the code does not break const in any way and therefore you are guaranteed (barring compiler bugs) that the data does not change.  If const were not machine-verifiable, it would be nothing more than programming by convention, since it would guarantee nothing.  Allowing const to be "broken" somewhere would mean it's no longer machine-verifiable (you need a human to verify whether the semantics are still correct).
>> 
>> This is still not true, it is not machine verifiable as it is. It can be bypassed quite easily, as a const object can be assigned from an non-const one. There's no way to offer that guarantee.
>
> You misunderstand. Const means "this code cannot modify this object no matter what".  It does not guarantee somebody else can't modify it (you want immutable for that).  Both mutable and immutable implicitly convert to const, therefore it is imperative that code that handles const never modifies the data, because you don't know the provenance of the data: it could have come from an immutable object.  Allowing const to "sometimes" modify stuff will violate immutable and cause UB.
>
> Whether a piece of code modifies the data is certainly machine-verifiable -- but only if there are no backdoors to const. If there are, then the compiler cannot feasibly verify const, since it would need to transitively examine all code called by the code in question, but the source code may not be always available.
>
> Even if the data came from a mutable object, it does not make it any less machine-verifiable, since what we're verifying is "this code does not modify this data", not "this data never changes".  For the latter, immutable provides that guarantee, not const.  It is possible, for example, to obtain a const reference to a mutable object, and have one thread modify the object (via the mutable reference) while another thread reads it (via the const reference).  You cannot guarantee that the data itself won't change, but you *can* guarantee that the code holding the const reference (without access to the mutable reference) isn't the one making the changes.
>
>
> T

Point being, there is a huge difference between what you were saying, and what you are saying now. "This data never changes" is a much better guarantee and check than "this code does not modify this data". You use const to make sure the data doesn't change, if you can't guarantee it doesn't change from any other code then I wouldn't say it is machine-verifiable.

So we would need another qualifier "tantamount" to be implemented then it seems.

August 28, 2018
Thanks, that's a good explanation of the point of the differences between const and immutable.
August 28, 2018
There's been some talk of adding a "mutable" qualifier for fields, which would stop the transitivity of const at that point. But it has problems, such as what happens with opaque types. The compiler can no longer check them, and hence will have to assume they contain mutable members.
August 28, 2018
On Tue, 28 Aug 2018 at 10:54, H. S. Teoh via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On Tue, Aug 28, 2018 at 10:20:06AM -0700, Manu via Digitalmars-d wrote: [...]
> > The reality is though, that D's const is not actually very useful, and C++'s const is.
>
> Actually, I think C++ const is not very useful, because it guarantees nothing. At the most, it's just a sanity checker to make sure the programmer didn't accidentally do something dumb.

I'd rate that as "pretty damn useful"™!

> But given an opaque
> C++ function that takes const parameters, there is ZERO guarantee that
> it doesn't actually modify stuff behind your back, and do so legally
> (per spec).

Well it can't modify the head-object... that's the point of head-const!

> I mean, how many times have you written const_cast<...> just to get a piece of code to compile?

Never in my life. That's a heinous crime. If it were removed from C++ and declared UB, I'd be fine with that.

> I know I've been guilty of this
> in many places, because it simply isn't worth the effort to track down
> all the places of the code that you need to fix to make it
> const-correct.  So basically, C++ const is nothing more than an
> annotation that isn't really enforced.

It could be enforced though. const_cast<> doesn't have to exist, and
`mutable` doesn't have to exist either.
That would strengthen C++'s design to make it more meaningful while
retaining a generally useful semantic.

That said, D's transitive const is a nice thing to be able to
express... I just recognise that it's mostly useless, and from that
perspective, I think being able to express the C++ meaning would be
useful, and certainly MORE useful.
I wonder if there's a design that could allow to express both options
selectively?

> But you're spot on about D's const, though.  While D's const *does* provide real guarantees (unless you tread into UB territory by casting it away), that also limits its scope so much that it's rarely useful outside of rather narrow confines.  Yet because it's so strict, using it requires investing significant effort.  So you end up with the unfortunate situation of "a lot of effort" + "limited usefulness" which for many people equals "not worth using".

And then that case of not being used (even if it could have) blocks
use somewhere else, and not(/unable-to)-const spreads like a virus >_<

> > D has no way to express head-const, and it turns out it's a tremendously useful concept.
>
> I can live without head-const... but what *really* makes const painful for me is the lack of head-mutable. I.e., given a const container (which implies const objects), there is no universal way to obtain a mutable reference to said const objects,

... I think we're talking about the same thing.
In this context, the container is the 'head', and the elements would
be mutable beneath that unless declared const themselves.

> unless you tread into UB territory by
> forcefully casting it away.  This makes const so limited in
> applicability that, for the most part, I've given up using const at all,
> in spite of having tried quite hard to use it as much as possible for
> years.

Right. This appears to be the accepted recommendation for quite some
time, and no change in sight.
Tragically, the more people resign to this recommendation (and it's
practically official at this stage), the harder it becomes to use even
if you want to; any library code that you interact with that didn't
use const because 'recommendation' creates interaction blockages for
your own code, propagating can't-use-const into your client code,
despite your best intentions.

D's const is an objective failure. I don't think anyone could argue otherwise with a straight face. It's sad but true; the surface area and complexity of the feature absolutely doesn't justify its limited (and actively waning) usefulness.

August 28, 2018
On Tue, 28 Aug 2018 at 19:00, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> There's been some talk of adding a "mutable" qualifier for fields, which would stop the transitivity of const at that point. But it has problems, such as what happens with opaque types. The compiler can no longer check them, and hence will have to assume they contain mutable members.

Exactly. And you arrive at C++.
'c-const' and 'turtles-const' probably need to be specified
differently from the top, not broken along the way with the likes of
mutable.
August 28, 2018
On 8/28/2018 6:39 AM, Iain Buclaw wrote:
> Template emission strategy is a mess, we're better off just instantiating all templates in all compilation units, and let the compiler decide whatever to discard. Even -allinst does not instantiate enough to allow the compiler to make such decisions that C++ has no problem with (most of the time).

Martin and I proposed a simple strategy for that, but Kenji implemented a different algorithm that nobody understands, and has proved inadequate. There are a couple unresolved bug reports on that.