I wish all qualifiers were revisited with an eye for simplification

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sun Aug 2 20:50:14 UTC 2020

(Background: qualifiers were introduced following my horror when I 
started writing D1 code and saw that strings are represented as char[]. 
So structs with string members would look like:

struct Widget
     int x, y;
     char[] name, parentName;

I found it just shocking that following a Widget's construction whoever 
aliased the same strings the outside could change members of the Widget 
without Widget knowing. Of course, other D1 coders disliked that as 
well, so they'd defensively duplicate in the constructor:

struct Widget
     int x, y;
     char[] name, parentName;
     this(char[] n, char[] p)
         name = n.dup;
         parentName = p.dup;

thus ensuring proliferation of the garbage whether duplication was 
needed or not.

I found this absolutely maddening, to the extent I didn't think D could 
be ever used at any considerable scale while dragging this anchor behind it.

The second problem was Walter was adamant about using arrays of 
characters at strings. He found the notion of a library-defined string 
type (a la C++) an absolute abomination. Stubborn about it like I've 
never seen him before or after. So my unstoppable requests for a string 
type were met with the proverbial immovable refusal. Ironically, much of 
his argument came from an efficiency angle, yet the unnecessary 
duplication was way less efficient than some reference counting/small 
string optimization/etc scheme that a dedicated string type would use.

Then we figured things would work out if we arranged things such that 
people could NOT change individual characters of a string. That would 
allow sharing without the danger of long-distance influence. After many 
discussions with Walter, Bartosz, Eric, Brad, and myself, immutable and 
const were born.

Then followed the other qualifiers, in order: shared and inout.)

* * *

The result is... there: https://dlang.org/spec/const3.html. It has the 
images https://dlang.org/images/qualifier-combinations.svg and 
https://dlang.org/images/qualifier-conversions.svg and a large table and 
a lot of rules. Whenever I code anything generic, I find myself going 
back to those damn images and tables way more than anyone ought to. (And 
it's ironic... I made those. Woe to the relative newcomer.)

It's all too complicated, making generic D programming into 3D chess 
instead of the difficult endeavor it already is. And what does it buy 
us? Well, we don't need to define a string library type. Yowzers. 
(Actually we should if we want to get rid of the GC. But then Walter 
would oppose that. So - stalemate once again.)

Far as I can tell, the power/weight ratio of qualifier is very poor. I 
wish a DIP would revisit qualifiers with a stated intent to simplify 
them as much as possible. Whenever I code generically I invariably run 
into these issues:

* Whatever I do, however I twitch, immutable finds the opportunity to 
lodge itself in a soft part of my body and cause constant pain. This 
doesn't work, that doesn't work. No solution for "tail immutable" - 
mutable references to immutable class instances can't be done without 
contortions. Can't assign to out immutable class references, though 
there's no reason for that (see Adam's recent post).

* No matter how I dice any significant piece of code, there will be five 
casts from immutable and/or back that I can't rid of and lose sleep at 
night trying to convince myself are justified.

* Every time "inout" comes within a radius of a mile of what I'm doing, 
it starts to stink like a skunk. I wish I could get a restraining order. 
I can't instantiate "inout" variables, so writing any tests or 
constraints becomes an advanced matter of defining functions and crap. I 
get frustrated, I protest to this forum, and immediately a cabal is 
raised under Timon's leadership. The cabal convinces me that inout is 
actually great and that I'm an idiot. I do get convinced, which is more 
of a proof that Timon is very good, than a testament to the conviviality 
of inout. Then I leave and get back to my code, and it stinks of inout 
again. And I hate it and myself for having to deal with it.

* Nobody - probably not even Timon - knows what "shared" does or is 
supposed to do and not do. The most I got from Walter ever is "shared is 
intentionally restricted so you can't do much without a cast". Yet the 
definition of "much" and the conditions under which casting is legit are 
not anywhere to be found.

* And of course, "shared" gladly partakes in qualifier combinations, 
thus spreading its stink around in a combinatorial manner. Believe it or 
not, the type "const inout shared T" exists. Of course, nobody knows 
what it really means or how it could be used.

A "Define All Qualifiers" DIP would be a radical improvement of the 
state of affairs.

More information about the Digitalmars-d mailing list