November 01, 2021
On Monday, 1 November 2021 at 18:38:05 UTC, Meta wrote:
> On Monday, 1 November 2021 at 08:15:55 UTC, Araq wrote:
>> On Monday, 1 November 2021 at 00:20:33 UTC, Andrei Alexandrescu wrote:
>>> I think it's relevant to our recent discussion on versioning, and also for its considerations on language design.
>>

Are you going to change your name to "Facebook"?
November 01, 2021
On Mon, Nov 01, 2021 at 06:02:26PM -0400, Andrei Alexandrescu via Digitalmars-d wrote:
> On 2021-11-01 17:41, Walter Bright wrote:
[...]
> > 4. The way scope & return & ref apply to types is completely consistent.  This enables me to advise that when people have problems understanding it, to rewrite their example code in terms of raw pointers, as it will behave the same.
> 
> I very much wish this is true and useful.

Sometimes I feel like Walter has a private copy of dmd that behaves differently from the one the rest of us use. `ref`, for example, due to its special treatment as a storage qualifier instead of a type constructor, is asymmetric w.r.t. other type ctors and therefore a source of endless special cases in generic code.  It's just a royal pain to work with.

And don't get us started on `auto ref`...


T

-- 
The best compiler is between your ears. -- Michael Abrash
November 01, 2021
On Mon, Nov 01, 2021 at 10:39:52PM +0000, bachmeier via Digitalmars-d wrote:
> On Monday, 1 November 2021 at 13:23:45 UTC, Arjan wrote:
> > On Monday, 1 November 2021 at 00:20:33 UTC, Andrei Alexandrescu wrote:
> > > I think it's relevant to our recent discussion on versioning, and also for its considerations on language design.
> > > 
> > > https://youtube.com/watch?v=raB_289NxBk
> > > 
> > > P.S. Destruction comes at https://youtu.be/raB_289NxBk?t=4727 :o)
> > 
> > Why is it that always when I see Herb Sutter talk about C++, that long lost love for C++ suddenly re-emerges from the deep hurt past relationship...
> 
> The same goes for Bjarne S, but then I open a C++ file in my text editor and images of Scott Meyers take over my brain.

LOL!!!


T

-- 
Never wrestle a pig. You both get covered in mud, and the pig likes it.
November 01, 2021
On 11/1/2021 3:02 PM, Andrei Alexandrescu wrote:
>> 1. basic types, structs, and arrays are interchangeable. But I couldn't add tuples to the list, because the function call ABI for structs is "special". Urgh.
> 
> 1a. Hash tables have a number of language magic hacks (particularly concerning keys that have statically-sized array types) that are thoroughly inconsistent with the language rules.

True, but hash tables are not basic types, structs, arrays or tuples!

>> 2. The nature of how const, immmutable, and shared are applied to types is completely consistent, despite many requests to change that.
> 
> 2a. Hash tables have any number of qualifier-related hacks that makes them impossible to implement as a library.
> 
> 2b. Calls to built-in arrays remove the top-level qualifier in a way that is not accessible to user code. This makes qualified ranges impossible to implement:
> 
> 
> void fun(T)(T data) {
>      import std;
>      writeln(T.stringof);
> }
> 
> void main() {
>      immutable int a = 42;
>      immutable int[] b = [ 69 ];
>      fun(a);  // fine, prints immutable(int)
>      fun(b);  // prints immutable(int)[], not immutable(int[])
> }

Maybe qualified ranges should be impossible to implement.

> 2c. There are a number of hacks in the compiler that give special treatment to shared numerics. Such is inaccessible to user code. Worse, they don't generate correct code.
> 
> void main() {
>      shared int i = 42;
>      i = i + 2;  // whatta?
> }

Shared memory reads/writes are no longer allowed. I don't think that's a hack.


>> 4. The way scope & return & ref apply to types is completely consistent. This enables me to advise that when people have problems understanding it, to rewrite their example code in terms of raw pointers, as it will behave the same.
> 
> I very much wish this is true and useful.

Show me where it isn't true and/or isn't useful.
November 01, 2021
On 11/1/2021 3:07 PM, Paul Backus wrote:
> On Monday, 1 November 2021 at 22:02:26 UTC, Andrei Alexandrescu wrote:
>> On 2021-11-01 17:41, Walter Bright wrote:
>>> 4. The way scope & return & ref apply to types is completely consistent. This enables me to advise that when people have problems understanding it, to rewrite their example code in terms of raw pointers, as it will behave the same.
>>
>> I very much wish this is true and useful.
> 
> It's true, as far as it goes. `return` and `scope` are consistent in the way they apply to *types*.
> 
> However, `ref` is not part of the type system--and when you add `ref` into the mix, that's when you start to see the special cases cropping up (see e.g. [Dennis's thread on the inconsistency of `return scope`][1]).
> 
> [1]: https://forum.dlang.org/thread/nbbtdbgifaurxoknyeuu@forum.dlang.org

That's the "return-scope-ref" ambiguity problem. I've put through a series of PRs to correct that. It's almost done. The underlying type system is correct, it's a syntactical issue.
November 02, 2021
On Monday, 1 November 2021 at 21:41:29 UTC, Walter Bright wrote:
> On 11/1/2021 9:18 AM, deadalnix wrote:
>> This part IMO, point at what the #1 problem with the way things have been done in D. D's feature just don't compose well because they aren't orthogonal with each other (and many are inconsistent).
>
> Can you be more specific?
>

Yes.

There are so many of them it's hard to list them all. I'm going to glance over a bunch on the top of my mind, feel free to ask more about any one of them if you want me to expand deeper.

1. enum binary ops and final switch. final switch assume an enum take a finite set of values as declared in the enum, but many more value can be obtained using binary ops.

2. type qualifier and delegates. Because the type system doesn't track the type qualifier of its capture, it is possible to have immutable or shared data refers to thread local ones through a delegate.

3. type qualifier transitivity and templates. template will consider types with different qualifier as disjoint, which seems like they should, until you try to write a container library or try to get range to interract with type qualifier.

This one is IMO the #1 problem with D, hands down. Nothing comes even remotely close. We don't have a decent set of collection to use because of it, and even worse, it's not even possible to write one.

4. Scope variables and closures. Closure capture sub scopes incorrectly. This is especially relevant in loops, because the variables captured are past their lifetime, leading to incorrect behaviors.

5. string autodecoding. The array type that behave like no other.

6. Identifier resolution and UFCS works prevent from effectively extending a type's behavior. This because as soon as the type is passed down to a template, then identifiers are now resolved in the template's context and all the UFCS extended interface becomes impossible to resolve.

7. Identifier resolution and mixin style codegen. mixin force a second round of identifier resolution, generated via T.stringof and alike, but these types may not resolve where the string is mixed in (for instance, when you mixin within a template).

8. 6 and 7 also do clash with visibility qualifiers, for the same reasons. Even when symbol resolve, they may end up being private.

9. Functional programming and attribute soup. Because it is not possible to define attribute in a way that depends to functions passed as argument, it is not possible to compose anything in a functional way without assuming that everything is impure, throw and uses the GC and more.

This breaks inout in an unfixable manner as it becomes impossible to disambiguate:

X foo(inout(Y) bar(...)) {} // Is the inout qualifier related to X or to bar's argument? Or both?

10. shared things are not constructible. dlang.org literally say you got to cast shared in and out, which is actually UB.

11. Function and first class function are different for no good reason. It's trivial to break a large portion of phobos by passing it functions that are not first class.

12. Sometime bool are integrals, sometime they aren't. Bonus point: one of the part of D that think bool are integral is DMD, and DMD won't hesitate to convert ints to bool if it can constant fold them into 0 or 1. But `isIntegral!bool` is false for instance.

You will note that few of the above behavior are wrong per se. They are just different part of D making different assumptions about the underlying reality of D, which are often fine in isolation, but are mutually exclusive.

For instance, there are good reasons for a bool to be an integral, and good reasons for it not to be. But there are no good reasons for DMD doig implicit conversion to bool as if was an integral, and `isIntegral!bool` being false. Each of them can be true, but both of them cannot be true at the same time.

These are just a few that I can brain dump, but there are a ton more. I'd say except 3. that is the elephant in the room, the overall thing is more a death by a thousand cut rather than

November 01, 2021
On 11/1/2021 3:02 PM, Andrei Alexandrescu wrote:
> 2b. Calls to built-in arrays remove the top-level qualifier in a way that is not accessible to user code.

Perhaps that should be applied generally. If someone wants to do a DIP for it, that would be good.
November 02, 2021
On Monday, 1 November 2021 at 22:58:53 UTC, ClapTrap wrote:
> On Monday, 1 November 2021 at 18:38:05 UTC, Meta wrote:
>> On Monday, 1 November 2021 at 08:15:55 UTC, Araq wrote:
>>> On Monday, 1 November 2021 at 00:20:33 UTC, Andrei Alexandrescu wrote:
>>>> I think it's relevant to our recent discussion on versioning, and also for its considerations on language design.
>>>
>
> Are you going to change your name to "Facebook"?

If only I had copyrighted my username!
November 01, 2021
On 11/1/21 9:18 PM, deadalnix wrote:
> On Monday, 1 November 2021 at 21:41:29 UTC, Walter Bright wrote:
>> On 11/1/2021 9:18 AM, deadalnix wrote:
>>> This part IMO, point at what the #1 problem with the way things have been done in D. D's feature just don't compose well because they aren't orthogonal with each other (and many are inconsistent).
>>
>> Can you be more specific?
>>
> 
> Yes.
> 
> There are so many of them it's hard to list them all. I'm going to glance over a bunch on the top of my mind, feel free to ask more about any one of them if you want me to expand deeper.
> 
> 1. enum binary ops and final switch. final switch assume an enum take a finite set of values as declared in the enum, but many more value can be obtained using binary ops.
> 
> 2. type qualifier and delegates. Because the type system doesn't track the type qualifier of its capture, it is possible to have immutable or shared data refers to thread local ones through a delegate.
> 
> 3. type qualifier transitivity and templates. template will consider types with different qualifier as disjoint, which seems like they should, until you try to write a container library or try to get range to interract with type qualifier.
> 
> This one is IMO the #1 problem with D, hands down. Nothing comes even remotely close. We don't have a decent set of collection to use because of it, and even worse, it's not even possible to write one.
> 
> 4. Scope variables and closures. Closure capture sub scopes incorrectly. This is especially relevant in loops, because the variables captured are past their lifetime, leading to incorrect behaviors.
> 
> 5. string autodecoding. The array type that behave like no other.
> 
> 6. Identifier resolution and UFCS works prevent from effectively extending a type's behavior. This because as soon as the type is passed down to a template, then identifiers are now resolved in the template's context and all the UFCS extended interface becomes impossible to resolve.
> 
> 7. Identifier resolution and mixin style codegen. mixin force a second round of identifier resolution, generated via T.stringof and alike, but these types may not resolve where the string is mixed in (for instance, when you mixin within a template).
> 
> 8. 6 and 7 also do clash with visibility qualifiers, for the same reasons. Even when symbol resolve, they may end up being private.
> 
> 9. Functional programming and attribute soup. Because it is not possible to define attribute in a way that depends to functions passed as argument, it is not possible to compose anything in a functional way without assuming that everything is impure, throw and uses the GC and more.
> 
> This breaks inout in an unfixable manner as it becomes impossible to disambiguate:
> 
> X foo(inout(Y) bar(...)) {} // Is the inout qualifier related to X or to bar's argument? Or both?
> 
> 10. shared things are not constructible. dlang.org literally say you got to cast shared in and out, which is actually UB.
> 
> 11. Function and first class function are different for no good reason. It's trivial to break a large portion of phobos by passing it functions that are not first class.
> 
> 12. Sometime bool are integrals, sometime they aren't. Bonus point: one of the part of D that think bool are integral is DMD, and DMD won't hesitate to convert ints to bool if it can constant fold them into 0 or 1. But `isIntegral!bool` is false for instance.
> 
> You will note that few of the above behavior are wrong per se. They are just different part of D making different assumptions about the underlying reality of D, which are often fine in isolation, but are mutually exclusive.
> 
> For instance, there are good reasons for a bool to be an integral, and good reasons for it not to be. But there are no good reasons for DMD doig implicit conversion to bool as if was an integral, and `isIntegral!bool` being false. Each of them can be true, but both of them cannot be true at the same time.
> 
> These are just a few that I can brain dump, but there are a ton more. I'd say except 3. that is the elephant in the room, the overall thing is more a death by a thousand cut rather than


I quote this post in its entirety because I so much agree with it. Each of these issues is something that has been overlooked for decades.
November 01, 2021
Thank you. This is a great list. I would appreciate it if you could make a bugzilla entry for each one, along with a piece of example code.

Some specific comments below:

On 11/1/2021 6:18 PM, deadalnix wrote:
> 1. enum binary ops and final switch. final switch assume an enum take a finite set of values as declared in the enum, but many more value can be obtained using binary ops.

Not sure what to say here. enums have more than one use:

1. enumerate all values of a particular type
2. enumerate some specific values of a particular type

Only use final for case (1).

> 2. type qualifier and delegates. Because the type system doesn't track the type qualifier of its capture, it is possible to have immutable or shared data refers to thread local ones through a delegate.

This is simply a bug I haven't gotten around to fix.


> 3. type qualifier transitivity and templates. template will consider types with different qualifier as disjoint, which seems like they should, until you try to write a container library or try to get range to interract with type qualifier.
> 
> This one is IMO the #1 problem with D, hands down. Nothing comes even remotely close. We don't have a decent set of collection to use because of it, and even worse, it's not even possible to write one.

I beg of you and Andrei to produce a canonical example and place it in bugzilla. Perhaps I misunderstood Andrei when he said it would be resolved when we had copy constructors. Well, we have copy constructors now.


> 4. Scope variables and closures. Closure capture sub scopes incorrectly. This is especially relevant in loops, because the variables captured are past their lifetime, leading to incorrect behaviors.
> 
> 5. string autodecoding. The array type that behave like no other.

That's actually not a problem with the language. It's a problem with Phobos, one I've been trying to kill with fire for 10 years no.


> 9. Functional programming and attribute soup. Because it is not possible to define attribute in a way that depends to functions passed as argument, it is not possible to compose anything in a functional way without assuming that everything is impure, throw and uses the GC and more.
> 
> This breaks inout in an unfixable manner as it becomes impossible to disambiguate:
> 
> X foo(inout(Y) bar(...)) {} // Is the inout qualifier related to X or to bar's argument? Or both?
> 
> 10. shared things are not constructible. dlang.org literally say you got to cast shared in and out, which is actually UB.

Yes, that's correct. I know of *no* way to construct a shared object without going into @system code. Just like there's no way to implement malloc() without dirty pointer manipulation. Note that in @safe code neither can be done. I propose that this is not a further issue.


> 12. Sometime bool are integrals, sometime they aren't. Bonus point: one of the part of D that think bool are integral is DMD, and DMD won't hesitate to convert ints to bool if it can constant fold them into 0 or 1. But `isIntegral!bool` is false for instance.

Phobos has a number of incomprehensible isxxxxx() functions. They are not the language's fault, though. Nor are they my fault, as I never reviewed them. But they are my responsibility.


> For instance, there are good reasons for a bool to be an integral, and good reasons for it not to be. But there are no good reasons for DMD doig implicit conversion to bool as if was an integral, and `isIntegral!bool` being false. Each of them can be true, but both of them cannot be true at the same time.

Of course. Part of the motivation for Phobos v2 is to get rid of that garbage.