May 10, 2021
On Monday, 10 May 2021 at 22:58:41 UTC, deadalnix wrote:
> My point is that we already have a language that is a mixed bag of accidentally defined features that don't compose properly with each others. I don't need one more of these, I already have one, and, let's be frank, it has at the very least an order of magnitude more support in the wild, in tools and so on.

Yes, I think everyone can agree with this. A good starting point would to implement proper unification of as was discussed some months ago. This is critical for composing types in a sensible manner (composing templates of templates and binding them to a simple name that is exported).

Then one can look and see if some types/features that are builtins can be expressed with the same building blocks in a unification process (somehow).

When you see what cannot fit into this machinery you get a feeling for which features needs to be redesigned.

Something like that.

> Doing the same thing with less manpower is a futile exercise.

Yes.


> Sure, but look at this thread. D is crumbling under the weight, not of the number f feature, but of the fact that a large portion of them simply are unsound.

Yes, but designing something that is sound is best done by having a tiny set of (theoretical) mechanisms that all other features can be expressed with (even though that might not be visible to the end user).

It is very difficult to even discuss soundness with no constructive framework to represent ideas with.

> Just look at what's in the C++ standard lib or boost and compare to your average C++ project to see the kind of gap in term of motivation to put up with bullshit exists between standard lib devs and Joe coder. It's not even close.

Yes, even stuff that is well designed in C++ is a lot of work. Implementing a new container library with all the iterators is quite verbose, tedious and typos will happen...

I think defining protocols and making mechanisms available that can extend types with protocols is the way to go (concepts is one step in the right direction). How to do it? Not sure, but it seems like templating by itself is not enough really.

E.g. if ranges-functionality should be available to everything that can be treated like a sequence, then this should be a protocol that is present in all the builtin types that are sequential. Or somehow bound to them in some global fashion (kinda like injected into the type). Nothing should be special cased. Ideally.

But there is no clear model for how to do that, I think.

However it is tied to unification. Deduce the protocol if possible.

May 11, 2021
On Monday, 10 May 2021 at 22:58:41 UTC, deadalnix wrote:

> At this point, the decision made is to push the madness on the user. Fair enough, but if the standard lib devs are not willing to put up with it, why in hell would you expect anyone else to? Just look at what's in the C++ standard lib or boost and compare to your average C++ project to see the kind of gap in term of motivation to put up with bullshit exists between standard lib devs and Joe coder. It's not even close.
>
> This stuff ain't working properly so let's just given getting to work at all is not how you iterate toward a great useful product.

+1

We *must* focus more on consistency and soundness imo. I've heard several users talk about this. So it's nice to see it being talked about here. The way for D forward is to polish up D2. Maybe have 2.100.0 as a goal. Like any project, it needs milestones. We should take a pause, look around and see, we're now in the "optimizing" phase. We can at least try. Then after 2.100.0 for example we can start talking about new cool features again.
May 11, 2021
I think it makes possible sense to require either wrappers that clarify intent, or always treat enums the same way (as an enum). I think Phobos *mostly* does the latter. Erroring for ambiguity might be more disruptive than it's worth.

May 11, 2021
On 5/10/21 8:19 AM, deadalnix wrote:
> On Monday, 10 May 2021 at 04:21:34 UTC, Andrei Alexandrescu wrote:
>> So you have a range r of type T.
>>
>> You call r.popFront().
>>
>> Obvioulsly the type of r should stay the same because in D variables don't change type.
>>
>> So... what gives, young Padawan?
>>
>> No, this is not subtyping 101.
> 
> If you have a range of T, then you got to return a T.

There's no return. The range is being mutated.

> I'm not sure what's the problem is here. Do you have a concrete example?

Of course. A range must implement popFront with the signature:

void popFront(ref SomeEnumString s) {
    ... please fill in the implementation ...
}
May 11, 2021
On Tuesday, 11 May 2021 at 12:05:18 UTC, Andrei Alexandrescu wrote:
>> I'm not sure what's the problem is here. Do you have a concrete example?
>
> Of course. A range must implement popFront with the signature:
>
> void popFront(ref SomeEnumString s) {
>     ... please fill in the implementation ...
> }

That must be a type error, this is a feature, not a bug. This is not expected to work.

May 11, 2021
On 5/10/21 1:09 PM, Joseph Rushton Wakeling wrote:
> On Monday, 10 May 2021 at 04:21:34 UTC, Andrei Alexandrescu wrote:
>>> Popping the head out of an enum value ought to be a string, not that enum's value. I don't really see where the problem is here, this is subtyping 101.
>>
>> So you have a range r of type T.
>>
>> You call r.popFront().
>>
>> Obvioulsly the type of r should stay the same because in D variables don't change type.
>>
>> So... what gives, young Padawan?
>>
>> No, this is not subtyping 101.
> 
> This feels a bit like the real problem might be in the conflation of the container (the enum or the string) and the range?
> 
> Cf. the way this is handled in Rust, where there is a clear distinction between a container, versus an iterator over that container:
> https://doc.rust-lang.org/rust-by-example/flow_control/for.html
> 
> Note also the different ways that the iterator can be generated: either using a reference to the container itself, or by moving the container into the iterator so the container itself is consumed by the iteration.

True, D has only "orphan" ranges, no containers. std.container is not working out and with current D technology we can't define containers that work with safe/pure/nogc at the same time (two out of three we can).

If you consider the enum string value a container and the string extracted from it a range of that container, I think that would be a valid way to look at the matter.
May 11, 2021
On 5/10/21 5:44 PM, deadalnix wrote:
> On Monday, 10 May 2021 at 12:19:07 UTC, deadalnix wrote:
>> On Monday, 10 May 2021 at 04:21:34 UTC, Andrei Alexandrescu wrote:
>>> So you have a range r of type T.
>>>
>>> You call r.popFront().
>>>
>>> Obvioulsly the type of r should stay the same because in D variables don't change type.
>>>
>>> So... what gives, young Padawan?
>>>
>>> No, this is not subtyping 101.
>>
>> If you have a range of T, then you got to return a T. I'm not sure what's the problem is here. Do you have a concrete example?
>>
>> All I can think of are things like slicing and alike, and they should obviously return a string, not a T.
> 
> More to the point, consider this:
> 
> class String {
> private:
>      immutable(char)[] value;
> 
> public:
>      this(immutable(char)[] value) { this.value = value; }
> 
>      // ...
> }
> 
> class EnumString : String {
> public:
>      static EnumString value1() { return new EnumString("value1"); }
>      static EnumString value2() { return new EnumString("value2"); }
> 
> private:
>      this(immutable(char)[] value) { super(value); }
> }
> 
> While the implementation differs, conceptually, from a the theory standpoint, this is the same.

No it isn't.

EnumString and String are reference types. A reference to an enum value does not convert to a reference to its representation. Very very very VERY different.

> This is using a subtype to constrain instance of type (String here) to a certain et of possible values. When using the subtype (EnumString) you have the knowledge that it is limited to some value, and you lose that knowledge as soon as you convert to the parent type.

One question that you keep not answering (Paul and I both asked it) is how you'd implement the range primitive popFront.

> But instead, we gets some bastardised monster from the compiler, that's not quit a subtype, but that's not quite something else that really make sens either. As expected, this nonsense ends up spilling into user code, and then the standard lib, based on user constraints, and everybody is left choosing between bad tradeof down the road because the whole house of cards is built on shaky foundations.
> 
> The bad news is, there is already a language like this. It's called C++, and it's actually quite successful. With all due respect to you and Walter, you guys are legends, but I think there is also a bit of learned helplessness coming from both of you due to a lifetime of exposure to the soul corroding effects of C++.
> 
> This attitudes pervades everything, and most language constructs suffer of some form of it in one way or another, causing a cascade of bad side effects, starting with this whole thread. A few examples en vrac for instance: DIP1000, delegate context qualifiers, functions vs first class functions, etc...

I very much agree Walter and I have brought C++ bias into D, sometimes in a detrimental way.

> Back to the case of enum, it is obviously and trivially a subtype.

No it isn't. How many times do I need to explain that?

> In fact, even the syntax is the same:
> 
> enum Foo: string { ... }

It doesn't matter. It's not a subtype.

> Handling enum strings should never have been a special that was added to phobos, because it should never have been a special to begin with, in phobos or elsewhere.

Clearly enums have their own oddities, most inherited from C++. Perhaps we should do what C++ did, add a new "enum class" construct that fixes its issues. But I don't know of a perfect design, and I very much would love to see one.
May 11, 2021
On 5/10/21 6:58 PM, deadalnix wrote:
>>
> 
> Sure, but look at this thread. D is crumbling under the weight, not of the number f feature, but of the fact that a large portion of them simply are unsound.
> 
> At this point, the decision made is to push the madness on the user. Fair enough, but if the standard lib devs are not willing to put up with it, why in hell would you expect anyone else to? Just look at what's in the C++ standard lib or boost and compare to your average C++ project to see the kind of gap in term of motivation to put up with bullshit exists between standard lib devs and Joe coder. It's not even close.
> 
> This stuff ain't working properly so let's just given getting to work at all is not how you iterate toward a great useful product.

In case you're referring to deprecating support for enum strings in phobos - definitely that's not pushing any madness anywhere. Adding said support was a mistake in the first place.
May 11, 2021
On 5/11/21 8:14 AM, deadalnix wrote:
> On Tuesday, 11 May 2021 at 12:05:18 UTC, Andrei Alexandrescu wrote:
>>> I'm not sure what's the problem is here. Do you have a concrete example?
>>
>> Of course. A range must implement popFront with the signature:
>>
>> void popFront(ref SomeEnumString s) {
>>     ... please fill in the implementation ...
>> }
> 
> That must be a type error, this is a feature, not a bug. This is not expected to work.
> 

Then enum strings are not ranges, correct?
May 11, 2021
On Tuesday, 11 May 2021 at 12:14:42 UTC, deadalnix wrote:
> On Tuesday, 11 May 2021 at 12:05:18 UTC, Andrei Alexandrescu wrote:
>>> I'm not sure what's the problem is here. Do you have a concrete example?
>>
>> Of course. A range must implement popFront with the signature:
>>
>> void popFront(ref SomeEnumString s) {
>>     ... please fill in the implementation ...
>> }
>
> That must be a type error, this is a feature, not a bug. This is not expected to work.

I realize that this require further explanations.

The fact that B is a subtype of A doesn't imply that a type constructed from B is a subtype of that same construction using A. For instance,

A function() would be a subtype of B function(), the relation reversed in that example.

In your example, you are constructing a ref SomeEnumString and expecting it to be a subtype of string (or maybe ref string) but both are incorrect assumptions. This is because you can execute operation that require covariance as well as operation that require contravariance on a ref, therefore, it needs to be exactly the same type. This is hardly an exceptional situation, this also happens when taking an array, B being a subtype of A doesn't mean the B[] is a subtype of A[].

Interestingly, it is the case for const ref, or const arrays, which is where the push toward handling const ref differently comes from.

In any case, it is not expect from format that it modify teh pattern it takes as an input. In fact, it is a god damn compile time parameter, it is not mutable to begin with. It is therefore expected that this works.