March 05, 2018
On Monday, 5 March 2018 at 13:49:43 UTC, Jonathan M Davis wrote:
> On Monday, March 05, 2018 11:38:05 Atila Neves via Digitalmars-d-announce wrote:
>> I used to use `immutable`, but gradually came around to only using it if I have to send data to another thread, otherwise it's too much of a hassle.
>
> Aside from the whole std.concurrency situation, I generally use immutable in the places where the semantics are the same as const, so it doesn't generally cause a problem for me. I just prefer immutable in the cases where it and const are the same, because immutable clearly indicates that the value never changes, so immutable more closely fits the idea even if const happens to mean that in that particular case. I agree that using immutable where you have to do stuff like design objects in a particular way in order to make immutable work is generally too much of a pain to be worth it, but at least in those cases, the data can then be shared across threads, and it's data that doesn't need mutable backdoors, because it only makes sense to use immutable in that fashion when the object is really designed to never change state.
>
> I think that the only time that I've programmed heavily in a way that involves immutability everywhere is when I programmed in Haskell, and that combined with the fact that Haskell is lazy and completely functional such that you can't use imperative idioms anywhere made it so that each line of Haskell code took me more time to write than is the case with any other language I've used (except maybe batch, because of how insanely error-prone it is). I think that I'm a better programmer for it, but I'd hate to program that way normally, and using immutable all over the place would head too heavily in that direction - though at least D, unlike Haskell, is a multi-paradigm language and allows you to choose to use a particular idiom when you think it makes the most sense instead of forcing it everywhere.
>
> - Jonathan M Davis

I prefer const over immutable because this compiles:

    string[] arr;
    const _ = arr;

While this does not:

    string[] arr;
    immutable _ = arr;


Basically, const just works and immutable is a hassle unless threads are involved. There are other situations where immutable is needed / should be preferred as well, notably:

struct Foo {
    const(ubyte)[] bytes;
}

Construct Foo from a mutable buffer that you're reusing to get network traffic and you're going to have a bad time.

Atila
March 05, 2018
On Monday, 5 March 2018 at 10:57:35 UTC, Jonathan M Davis wrote:
> Here's something I wrote up on const:
>
> http://jmdavisprog.com/articles/why-const-sucks.html
>
> I suppose that it's not exactly the most positive article, but I feel that it's accurate.
>
> - Jonathan M Davis

Interesting read and it explains some of the incomprehensible error messages.

Basically you're saying we've got a strong const in D but essentially the situation is much like in Java because the general approach is don't use it ? Kind of ironic.

I made a Logger module a couple years back and I used const very generously.
Then came the moment where the LogWriters actually had to do the writing.
One would assume that a function called 'stdio.write' mutates some state, but it sure as hell doesn't affect the state of my logger.

DMD said: So soooowy! writeln not const-y, no can do, soooowy!
So I remove const. One function at a time and end up with a module where basically no const remains.

But it doesn't stop there. Since logger isn't const anymore, it now can't be used in any const functions. So more de-const-ification happened...

Also, it's frustrating to have to get rid of const because Object.toString uses some Outputwriter thing which is not const and therefore transitively you can't have const anywhere else.

Kind of a disappointing experience.

As far as I'm concerned, it's not so much "don't use const; or, it's not worth it" but more like "I would if I could but I can't so I shan't".

I bother with const as much as possible but it's incredible frustrating and I would argue the time lost to de-const-ify APIs is easily equivalent to a code breaking change that would make const more applicable.

Something possimpible. Like a compiler that doesn't purely make decisions upon a const keyword, but one that can understand that interacting with some random function with only "in" parameters or objects, which aren't even interacting with,or are part of, the object's internal state and are just passed through, can't violate the const-ness of the object.

But there's likely much more to consider than that and I couldn't ever dream of implementing such a thing..unfortunately
March 05, 2018
Very interesting and well written! Jonathan, your experiences with const in C++/Java just about matches my experiences with it. I also feel that the situation in D is less than ideal in this regard.

First, a small (for sure copy-pasta) typo in your article:

const(int[]) arr1 = getArray();
const(int)[] arr2 = p1;  //Sure you meant arr2 = arr1;

Regarding the proposed solution to the issues with Object namely "the solution to this problem which was agreed to a few years ago was to remove opEquals, opCmp, toHash, and toString from Object so that derived classes can define them with whatever attributes are desired" which, you say, will never happen because of too much code breakage...

What about the following? Currently the compiler is smart enough, so you can define class Foo and explicitly inherit from Object if you prefer:
class Foo : Object {..} //OK, no recursive Object inherits Object ad-infinitum.

Would it not be easier to exploit this and go the "opposite" way i.e. rather than remove it, just extend the hierarchy by adding a base class to Object itself (in object.d no less), something like this:

class ObjectBase
{
    interface Monitor{..}

    static ObjectBase factory(string classname){..}
}


class Object : ObjectBase
{
    string toString(){..}
    size_t toHash() @trusted nothrow{..}
    int opCmp(Object o){..}
    bool opEquals(Object o){..}

    static Object factory(string classname){..}
}

Now, if you do:

class Foo{..} //OK. Still inherits from Object i.e. identical to 'class Foo : Object {..}
class Foo : Object {..} //The same, just explicit
//On the other hand:
class Bar : ObjectBase //Deliberately bypass inheritance from Object.

Inheritance from ObjectBase will have to be explicit (the price to pay for non-breakage!), but now class Bar is free to implement opEquals, opCmp, toHash, etc as it sees fit. This still guarantees back-wards compatibility since all classes currently inherited from Object have exactly the same semantics as they do today. No upgrades to any current projects/code required!

Why was something like this not considered (you don't give a link, so I cannot investigate), rather than simply removing them from Object? Can this be exploited to, in effect, create the same opportunity but minus the breakage? Or am I missing something?

Actually, in a more general sense I have begun to wonder if the common point of departure in D-land, that the concept of tail-const is actually a "part of" the concept of const, is not maybe wrong. We typically describe the concepts of tail/head-const "in terms of" const i.e. const in D 'equals' head-const 'plus' tail-const [1]. Since the concepts of tail-const and head-const are not that well known (outside of mostly C++-land), these 2 concepts are often utilized to differentiate D's concept of const, and explain it relative to the const (or final/sealed/readonly/etc) found in other languages. Maybe that muddles the water, and the 3 concepts, even though related, can semantically exist separately as well!

I am of the opinion that we really need something like tail-const in D - particularly to semantically mark that the "payload" in a range is constant, but that the range itself can mutate. But it invariably falls apart when you try to shoehorn it into the general concept of const-ness in D with its transitivity properties, etc. The 2 concepts just don't gel, and I think the only way this can be accomplished is to have a separate semantic construct and syntax for tail-const. I think the 2 use cases are mostly orthogonal, and only overlapping in a narrow sense - but it this narrow sense that is used to explain it!. And, yes, the tail-const version will have less guarantees and none of the optimization opportunities that the current const version can promise.

Of course I realize the changes of this being added to D is practically zero; So, yes, i have to concur with your conclusions as well!

PS. BTW, your blog's title "The Long-Winded D Guy" is quite brilliant. You are indeed a bit long-winded, but you also take the time to explain things very thoroughly. It is a trade-off and I think in your case the cost-benefit ratio is positive. Thank you for that! Also, if you ever write a book (it might have to come in multiple volumes ;-), you can already count on having 1 buyer!

[1] https://dlang.org/articles/const-faq.html#const

March 05, 2018
On Monday, March 05, 2018 17:35:28 ShadoLight via Digitalmars-d-announce wrote:
> Very interesting and well written! Jonathan, your experiences with const in C++/Java just about matches my experiences with it. I also feel that the situation in D is less than ideal in this regard.
>
> First, a small (for sure copy-pasta) typo in your article:
>
> const(int[]) arr1 = getArray();
> const(int)[] arr2 = p1;  //Sure you meant arr2 = arr1;

Thanks. Fixed.

> Regarding the proposed solution to the issues with Object namely "the solution to this problem which was agreed to a few years ago was to remove opEquals, opCmp, toHash, and toString from Object so that derived classes can define them with whatever attributes are desired" which, you say, will never happen because of too much code breakage...
>
> What about the following? Currently the compiler is smart enough, so you can define class Foo and explicitly inherit from Object if you prefer:
...
> Why was something like this not considered (you don't give a link, so I cannot investigate), rather than simply removing them from Object? Can this be exploited to, in effect, create the same opportunity but minus the breakage? Or am I missing something?

As I mentioned, there was recently some talk about creating a DIP to add a new root object below Object. If that's done, presumably, Object will still be the default to avoid code breakage, but it would be provide essentially what you're talking about. However, such a DIP still has to be written, so anything at this point is speculation as to what it's going to look like. At the time that it was decided to remove the functions from Object, it was more reasonable than it would be now (since D is definitely older now with a larger user base), and the details of how it would be done were never fully decided, which is part of why it's never come to fruition in spite of it being clear that we needed a root object without those functions.

- Jonathan M Davis

March 05, 2018
 On Monday, 5 March 2018 at 13:48:23 UTC, Adam D. Ruppe wrote:
> Just a semantic note, it is "straitjacket". "straight" is like a non-wiggly line. "strait" means narrow or constricted. Thus, the straitjacket is a jacket that constricts your movement.
>
> Of course, using "straight" is such a common mistake it has become generally accepted... but still, I like being precise with my words.


    You guys are a bunch of nerds . . . .
March 05, 2018
On Monday, 5 March 2018 at 13:48:23 UTC, Adam D. Ruppe wrote:
> Just a semantic note, it is "straitjacket". "straight" is like a non-wiggly line. "strait" means narrow or constricted. Thus, the straitjacket is a jacket that constricts your movement.
>
> Of course, using "straight" is such a common mistake it has become generally accepted... but still, I like being precise with my words.


    You guys are a bunch of nerds . . . .


- Me
March 05, 2018
On Monday, 5 March 2018 at 13:48:23 UTC, Adam D. Ruppe wrote:
> Just a semantic note, it is "straitjacket". "straight" is like a non-wiggly line. "strait" means narrow or constricted. Thus, the straitjacket is a jacket that constricts your movement.
>
> Of course, using "straight" is such a common mistake it has become generally accepted... but still, I like being precise with my words.

Programmers like precision, don't we! From Simon Tatham article about how to report bugs effectively: https://www.chiark.greenend.org.uk/~sgtatham/bugs.html

"Above all, *be precise*. Programmers like precision."
March 06, 2018
On Monday, 5 March 2018 at 10:57:35 UTC, Jonathan M Davis wrote:
> Here's something I wrote up on const:
>
> http://jmdavisprog.com/articles/why-const-sucks.html
>
> I suppose that it's not exactly the most positive article, but I feel that it's accurate.
>
> - Jonathan M Davis

Its amazing how typed languages, in this case const, forces you to think in mutation but with an exception. I wonder the real world SOLE benefit of using const compared to mutable-immutable style. Its either mutable or immutable. Exception could only available by casting immutable to mutable.

I feels this const thing is a theoretical problem. One can overcome practically with code style discipline. At a point, const becomes just a decoration; something people use prematurely (YAGNI).

It must feel like sleeping on your bed with sharp knives hanging at the top such that they can fall on you anything: when comparing code in a typed language to an untyped one. That's a potential cause of premature use of certain type attributes.


By the way, Jonathan, you should really consider writing a book with such deep insights your have. Especially the way you explain things.
March 06, 2018
On Monday, 5 March 2018 at 13:59:02 UTC, Adam D. Ruppe wrote:
> On Monday, 5 March 2018 at 10:57:35 UTC, Jonathan M Davis wrote:
>> Here's something I wrote up on const:
>
> And then, of course, like you said "don't use it" is the solution to most of the const system's flaws anyway.... which seems to be the case with a lot of D's add-on qualifiers.

+1
The worst part of those add-on qualifiers is that the official gospel is that they are useful when in reality you HAVE to avoid them to be productive.
March 06, 2018
On Monday, 5 March 2018 at 10:57:35 UTC, Jonathan M Davis wrote:
> Here's something I wrote up on const:
>
> http://jmdavisprog.com/articles/why-const-sucks.html
>
> I suppose that it's not exactly the most positive article, but I feel that it's accurate.
>
> - Jonathan M Davis

Spot on article, and touches some of my pain points when working with const/immutable structs.

Recently I tried to create a ref-counted immutable struct, oh boi...

This later use case is of tremendous value for safe concurrent code that's @nogc. Unfortunately I couldn't find a way to make it work efficiently and in the same time not look like a disgusting hack.

I suspect a possible solution is to allow immutable(const) postblit overloads as well as immutable dtors that will act as an escape hatch for unsafe work and in the same time provide hints that you are operating on an immutable(const) this.