August 03, 2020
On 8/2/20 6:22 PM, Andrei Alexandrescu wrote:
> On 8/2/20 5:30 PM, Adam D. Ruppe wrote:
>> On Sunday, 2 August 2020 at 20:50:14 UTC, Andrei Alexandrescu wrote:
>>> The cabal convinces me that inout is actually great and that I'm an idiot.
>>
>> inout is great, but you are also not wrong about it. It has frustrating arbitrary limitations that should really just get lifted.
> 
> Yes that would help a lot.

If I could hack dmd, I would make this my first priority...

-Steve
August 03, 2020
On Monday, 3 August 2020 at 11:45:22 UTC, Simen Kjærås wrote:
> On Sunday, 2 August 2020 at 20:50:14 UTC, Andrei Alexandrescu wrote:
>> [...]
>
> Shared today is a half-baked implementation of a half-thought idea. Manu wrote[0] almost two years ago what it should be: you can't read or write from a shared object, and only shared methods may be called on it. This was made into a DIP[1], and is half-way(?) implemented in the compiler as -preview=nosharedaccess. The semantics are effective and easy to understand.
>
> --
>   Simen
>
> [0]: https://forum.dlang.org/post/mailman.4299.1539629222.29801.digitalmars-d@puremagic.com
> [1]: https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1024.md

Very much looking forward to activating shared with -preview=nosharedaccess.  Will do so widely once "hello world" compiles again.

August 04, 2020
On Mon, Aug 3, 2020 at 6:55 AM Andrei Alexandrescu via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> (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.
>

Shared recently received a `-preview` which makes it really mean something;
this is what shared means:
1. The data is shared; therefore, it is invalid to read or write that
memory.
2. The reason this is useful as an attribute, is because you are able to
attribute methods. Ability to author a set of threadsafe methods and
clearly distinguish them from non-threadsafe methods is a powerful
advantage over C++.

#2 isn't well accepted, although I've been pushing that for years. If #2
isn't strengthened, then it is my opinion that `shared` is completely
useless, and `struct Shared(T) { private: T* sharedThing; }` is just as
good.
I think shared has potential to offer critical benefit to D, and we've
talked about this personally to some length. Don't kill it yet, let's try
and fix it. Although it's worth recognising that if we don't fix it, then
it might as well be killed as it stands today.


August 03, 2020
On 8/3/20 5:56 PM, Manu wrote:
> I think shared has potential to offer critical benefit to D, and we've talked about this personally to some length. Don't kill it yet, let's try and fix it. Although it's worth recognising that if we don't fix it, then it might as well be killed as it stands today.

Of course fixing it would be great! I'm glad you pushed the restriction through. At least now generic code could see `shared` as "shrouded in opacity, not subject to the usual operations".
August 04, 2020
On Monday, 3 August 2020 at 21:56:34 UTC, Manu wrote:
> Shared recently received a `-preview` which makes it really mean something;
> this is what shared means:
> 1. The data is shared; therefore, it is invalid to read or write that
> memory.
> 2. The reason this is useful as an attribute, is because you are able to
> attribute methods. Ability to author a set of threadsafe methods and
> clearly distinguish them from non-threadsafe methods is a powerful
> advantage over C++.

For some reason I often end up with multiple threads and the coordination that comes with it. Shared has been very helpful for me and I am using no. 2 with good success.

There is just one thing about shared I don't understand. If I design my object such that the non-shared methods are to be used thread-local and the shared methods from any thread, it follows that I should be able to call said shared methods from both a shared and non-shared instance of that object.

Often I workaround it be introducing a non shared method that forwards to the shared method by means of casting.

> #2 isn't well accepted, although I've been pushing that for years.

Thanks!

> I think shared has potential to offer critical benefit to D, and we've
> talked about this personally to some length. Don't kill it yet, let's try
> and fix it. Although it's worth recognising that if we don't fix it, then
> it might as well be killed as it stands today.

There might be room for improvement, but it is useful for me already. For instance, I often deal with delegates that can be called from any execution context. I make them shared to convey those semantics and the compiler even helps! Very nice.
August 04, 2020
On Tuesday, 4 August 2020 at 07:38:26 UTC, Sebastiaan Koppe wrote:
> On Monday, 3 August 2020 at 21:56:34 UTC, Manu wrote:
>> Shared recently received a `-preview` which makes it really mean something;
>> this is what shared means:
>> 1. The data is shared; therefore, it is invalid to read or write that
>> memory.
>> 2. The reason this is useful as an attribute, is because you are able to
>> attribute methods. Ability to author a set of threadsafe methods and
>> clearly distinguish them from non-threadsafe methods is a powerful
>> advantage over C++.
>
> For some reason I often end up with multiple threads and the coordination that comes with it. Shared has been very helpful for me and I am using no. 2 with good success.
>
> There is just one thing about shared I don't understand. If I design my object such that the non-shared methods are to be used thread-local and the shared methods from any thread, it follows that I should be able to call said shared methods from both a shared and non-shared instance of that object.
>
> Often I workaround it be introducing a non shared method that forwards to the shared method by means of casting.

With DIP1024, any type should be implicitly castable to shared. This means you should always be able to call shared methods on any object. Far as I can see, this is not implemented.

For a type with no thread-safe interface that means anyone with a shared reference to it can call absolutely no methods on it, and not read or write any of its fields. Kinda useless, but that's the point. If you want to manipulate it, you will need to cast away shared, which is non-@safe and a red flag.

For a type with a thread-safe interface, there may be methods that can't be called, but some subset will be callable. These must be written such that they are thread-safe, and any non-shared methods must be written such that they do not break the thread-safety of shared methods (but non-shared methods may be non-thread-safe if called on multiple threads, since that should not happen).

In other words, DIP1024 assumes you will have at most one non-shared reference to an object, and that no methods on an object are written in such a way that they break thread-safety under this assumption.

--
  Simen
August 04, 2020
On Tue, Aug 4, 2020 at 5:40 PM Sebastiaan Koppe via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Monday, 3 August 2020 at 21:56:34 UTC, Manu wrote:
> > Shared recently received a `-preview` which makes it really
> > mean something;
> > this is what shared means:
> > 1. The data is shared; therefore, it is invalid to read or
> > write that
> > memory.
> > 2. The reason this is useful as an attribute, is because you
> > are able to
> > attribute methods. Ability to author a set of threadsafe
> > methods and
> > clearly distinguish them from non-threadsafe methods is a
> > powerful
> > advantage over C++.
>
> For some reason I often end up with multiple threads and the coordination that comes with it. Shared has been very helpful for me and I am using no. 2 with good success.
>
> There is just one thing about shared I don't understand. If I design my object such that the non-shared methods are to be used thread-local and the shared methods from any thread, it follows that I should be able to call said shared methods from both a shared and non-shared instance of that object.
>
> Often I workaround it be introducing a non shared method that forwards to the shared method by means of casting.
>

Yes, this is a thing I talked at great length 1-2 years ago.
If you take shared to mean "is thread-safe", then my idea was that
not-shared -> shared implicit conversion should be possible.
What I often do is this:

struct Thing
{
  ref shared(Thing) implSharedCast() { return *cast(shared)&this; }
  alias implSharedCast this;
}

If that were an implicit conversion, that implies a slight change of
meaning of shared (to one that I consider immensely more useful), but it's
more challenging for the compiler to prove with confidence, and there's a
lot of resistance to this change.
In the mean-time, until the shared usage patterns are more well-proven, I
recommend you try to use the 'alias this' pattern I show above, and report
on any issues you encounter using this scheme. If no issues are identified
with extensive real-world usage data, I will push again for that implicit
conversion rule.


August 04, 2020
On Tue, Aug 4, 2020 at 6:26 PM Simen Kjærås via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Tuesday, 4 August 2020 at 07:38:26 UTC, Sebastiaan Koppe wrote:
> > On Monday, 3 August 2020 at 21:56:34 UTC, Manu wrote:
> >> Shared recently received a `-preview` which makes it really
> >> mean something;
> >> this is what shared means:
> >> 1. The data is shared; therefore, it is invalid to read or
> >> write that
> >> memory.
> >> 2. The reason this is useful as an attribute, is because you
> >> are able to
> >> attribute methods. Ability to author a set of threadsafe
> >> methods and
> >> clearly distinguish them from non-threadsafe methods is a
> >> powerful
> >> advantage over C++.
> >
> > For some reason I often end up with multiple threads and the coordination that comes with it. Shared has been very helpful for me and I am using no. 2 with good success.
> >
> > There is just one thing about shared I don't understand. If I design my object such that the non-shared methods are to be used thread-local and the shared methods from any thread, it follows that I should be able to call said shared methods from both a shared and non-shared instance of that object.
> >
> > Often I workaround it be introducing a non shared method that forwards to the shared method by means of casting.
>
> With DIP1024, any type should be implicitly castable to shared. This means you should always be able to call shared methods on any object. Far as I can see, this is not implemented.
>
> For a type with no thread-safe interface that means anyone with a shared reference to it can call absolutely no methods on it, and not read or write any of its fields. Kinda useless, but that's the point. If you want to manipulate it, you will need to cast away shared, which is non-@safe and a red flag.
>
> For a type with a thread-safe interface, there may be methods that can't be called, but some subset will be callable. These must be written such that they are thread-safe, and any non-shared methods must be written such that they do not break the thread-safety of shared methods (but non-shared methods may be non-thread-safe if called on multiple threads, since that should not happen).
>
> In other words, DIP1024 assumes you will have at most one non-shared reference to an object, and that no methods on an object are written in such a way that they break thread-safety under this assumption.
>
> --
>    Simen
>

I don't think what you describe has anything to do with DIP1024.
What you're describing is the scheme I was arguing for 1-2 years ago, but
Walter rejected it and presented DIP1024 instead.
DIP1024 was a step in the right direction, so I supported it at that time,
but it doesn't change any definitions about thread-safety of methods, and
it's not a complete solution. It just adds the restrictions that should
have been there from the start; that is, `shared` has no read or write
access to data.


August 04, 2020
On Tuesday, 4 August 2020 at 13:06:35 UTC, Manu wrote:
> On Tue, Aug 4, 2020 at 6:26 PM Simen Kjærås via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
[stuff]
> I don't think what you describe has anything to do with DIP1024.
> What you're describing is the scheme I was arguing for 1-2 years ago, but
> Walter rejected it and presented DIP1024 instead.
> DIP1024 was a step in the right direction, so I supported it at that time,
> but it doesn't change any definitions about thread-safety of methods, and
> it's not a complete solution. It just adds the restrictions that should
> have been there from the start; that is, `shared` has no read or write
> access to data.

You're right. I was under the impression that DIP1024 was going to do all the things in your proposal (and I still hope they will be added to D eventually), but it seems I was wrong.

--
  Simen
August 04, 2020
So, are you guys still surprised nobody wants to use D in production? The current language state is simply not suitable for development. D is just a bunch of features and most of them aren't even designed well. This stagnation has been going on for years without any improvement. It's time for D3.