May 07, 2021
On Friday, 7 May 2021 at 15:25:30 UTC, Andrei Alexandrescu wrote:
> Enums derived from strings should not be supported as strings in the standard library.

I don't think the stdlib should special case much of anything.

Special casing enums is a mistake. If the user wants it treated as a string, they can cast it to a string.

Special casing static arrays is a mistake. The user can just slice it out the outside.

Special casing alias this is a mistake. The user can pass what they meant to pass.

The phobos templates should work like all other templates - on the exact type passed. Other functions work with the normal overloading and implicit conversion rules.

Kill all the special cases!
May 07, 2021
On 5/7/21 11:33 AM, Adam D. Ruppe wrote:
> On Friday, 7 May 2021 at 15:25:30 UTC, Andrei Alexandrescu wrote:
>> Enums derived from strings should not be supported as strings in the standard library.
> 
> I don't think the stdlib should special case much of anything.
> 
> Special casing enums is a mistake. If the user wants it treated as a string, they can cast it to a string.

yes

> Special casing static arrays is a mistake. The user can just slice it out the outside.

Yes

> Special casing alias this is a mistake. The user can pass what they meant to pass.

YES

> The phobos templates should work like all other templates - on the exact type passed. Other functions work with the normal overloading and implicit conversion rules.
> 
> Kill all the special cases!

YES!!!
May 07, 2021
On 5/7/21 11:30 AM, Andrei Alexandrescu wrote:
> On 5/7/21 11:20 AM, Steven Schveighoffer wrote:
>> If you mean we shouldn't support it (as an ambiguous case) in *conversion* utilities (i.e. to/from string), then this makes some sense. But it's also not straightforward. Sometimes you WANT to convert from the enum to the base type. Sometimes you want to convert to the enum name. Going backwards (string to enum), which one makes more sense? It depends on context. It also doesn't help that a string enum implicitly converts to a string. The language is going to circumvent any policies Phobos has on that front.
> 
> Enums are poorly designed, but that's only a small part of the problem.
> 
> The bigger problem is the corruption of a noble principle. We wanted to be as generic as possible, and indeed in the beginning that seemed not only possible, but also easy. I don't think there's any other language or library supporting different character widths with this little aggravation.
> 
> Then this whole "be as generic as possible" became a slippery slope of inclusion. Allow enum strings. Allow alias this strings.

But an enum with base string type can be passed as a string. The PR in question is working around a limitation of the Phobos trait that says something derived from a string isn't really usable as a string (when it is).

The problem I see is, when phobos says something isn't true, when it really is, causes no end of confusion (*cough* autodecoding)

static assert(!isSomeString!T);
// yet...
string s = someT;

> 
> How about no.
> 
> User: "I have this enum string str and phobos won't consider it a string. Help!"
> 
> Another user: "Just use str.representation if you want to pass str around as a string."
> 

User: "OK, but when should I use representation? I already pass it around as a string and it works fine. Why can't phobos comprehend that, when the language has no problems with it?"

-Steve
May 07, 2021
On Friday, 7 May 2021 at 15:51:39 UTC, Steven Schveighoffer wrote:
> But an enum with base string type can be passed as a string.

"Can be passed as a" is not the same as "is a". There's a conversion involved.

For better or for worse, D templates do not participate in conversion and we shouldn't pretend that they do. This is often times very useful - you don't want to lose information in many templates. But there's other times when that information doesn't matter and it would be nice it you didn't have to think about it....

...so maybe we should consider changing templates so they can participate at the language level... it would be interesting if the compiler did the conversions BEFORE instantiating any template. Then it can reuse the instances more easily too. I think it actually does for const params for example, but it could do more.

> User: "OK, but when should I use representation? I already pass it around as a string and it works fine. Why can't phobos comprehend that, when the language has no problems with it?"

But the language DOES have problems with it for certain types of functions. Phobos is trying to deny that reality.
May 07, 2021
On 5/7/21 11:51 AM, Steven Schveighoffer wrote:
> On 5/7/21 11:30 AM, Andrei Alexandrescu wrote:
>> On 5/7/21 11:20 AM, Steven Schveighoffer wrote:
>>> If you mean we shouldn't support it (as an ambiguous case) in *conversion* utilities (i.e. to/from string), then this makes some sense. But it's also not straightforward. Sometimes you WANT to convert from the enum to the base type. Sometimes you want to convert to the enum name. Going backwards (string to enum), which one makes more sense? It depends on context. It also doesn't help that a string enum implicitly converts to a string. The language is going to circumvent any policies Phobos has on that front.
>>
>> Enums are poorly designed, but that's only a small part of the problem.
>>
>> The bigger problem is the corruption of a noble principle. We wanted to be as generic as possible, and indeed in the beginning that seemed not only possible, but also easy. I don't think there's any other language or library supporting different character widths with this little aggravation.
>>
>> Then this whole "be as generic as possible" became a slippery slope of inclusion. Allow enum strings. Allow alias this strings.
> 
> But an enum with base string type can be passed as a string. The PR in question is working around a limitation of the Phobos trait that says something derived from a string isn't really usable as a string (when it is).

Well you see here is the problem. An enum with base string can be coerced to a string, but is not a true subtype of string. This came to a head with ranges, too - you can pop off the head of a string still have a string, but if you pop off the head of an enum string you get some enum value that is not present in the set of enum values. Concatenation has similar problems, e.g. s ~ s for enum strings yields string, not an enum string. (Weirdly s ~= s works...)

So enum strings break ISA/Liskov. Alias this also does due to an overwhelming number of errors in its design and implementation.

> The problem I see is, when phobos says something isn't true, when it really is, causes no end of confusion (*cough* autodecoding)
> 
> static assert(!isSomeString!T);
> // yet...
> string s = someT;

This only shows that we have a baroque language that allows user-defined conversions from non-strings to strings. The code above is NO PROOF that T is supposed to be a string.

>> How about no.
>>
>> User: "I have this enum string str and phobos won't consider it a string. Help!"
>>
>> Another user: "Just use str.representation if you want to pass str around as a string."
>>
> 
> User: "OK, but when should I use representation? I already pass it around as a string and it works fine. Why can't phobos comprehend that, when the language has no problems with it?"

"When you want a string".

May 07, 2021
On 5/7/21 12:30 PM, Adam D. Ruppe wrote:
> On Friday, 7 May 2021 at 15:51:39 UTC, Steven Schveighoffer wrote:
>> But an enum with base string type can be passed as a string.
> 
> "Can be passed as a" is not the same as "is a". There's a conversion involved.

YES! Int is not floating point, but yes you can initiate a floating point from an int.

BTW it's worse than I feared. There are 104 occurrences of StringTypeOf in phobos. There should be 0.
May 07, 2021
On Friday, 7 May 2021 at 16:30:26 UTC, Adam D. Ruppe wrote:
>
> For better or for worse, D templates do not participate in conversion and we shouldn't pretend that they do. This is often times very useful - you don't want to lose information in many templates. But there's other times when that information doesn't matter and it would be nice it you didn't have to think about it....

We can already *almost* express this in the language. This code works:

    void fun(T : string, T val)() {
        pragma(msg, "instantiated with ", T.stringof);
    }

    enum E : string { x = "hello" }

    alias test = fun!(E, E.x);
    // prints: instantiated with E

But if you try to write it the more natural way, with the value parameter first, and have the compiler deduce the type, you get an error:

    void fun(T val, T : string)() {
        pragma(msg, "instantiated with ", T.stringof);
    }
    // Error: undefined identifier `T`
May 07, 2021
On 5/7/21 12:43 PM, Andrei Alexandrescu wrote:
> On 5/7/21 11:51 AM, Steven Schveighoffer wrote:
>> On 5/7/21 11:30 AM, Andrei Alexandrescu wrote:
>>> User: "I have this enum string str and phobos won't consider it a string. Help!"
>>>
>>> Another user: "Just use str.representation if you want to pass str around as a string."
>>>
>>
>> User: "OK, but when should I use representation? I already pass it around as a string and it works fine. Why can't phobos comprehend that, when the language has no problems with it?"
> 
> "When you want a string".
> 

Sorry, let's jump out of the fake dialog here for a second.

The problem I have is, you have a function like:

foo(T)(T s) if (isSomeString!T)

The *intention* here is that, I want to NOT have to write:

foo(string s) { impl }
foo(wstring s) { impl }
foo(dstring s) { impl }
... // etc with const, mutable

BUT, if I have an enum that converts to a string, then if I actually DID write all those, then it would compile. However, the template version does not. This is the confusion that a user and library author has.

I think the problem here is that the language doesn't give you a good way to express that. So we rely on template constraints that both can't exactly express that intention, and where the approximations create various template instantiations that cause strange problems (i.e. if you accept an enum that converts to string, it's still an enum inside the template). Whereas the language

I'm not suggesting any specific changes here, but I recognize there is a disconnect from what we *want* to express, and what the language provides.

-Steve
May 07, 2021
On 5/7/21 12:30 PM, Adam D. Ruppe wrote:
> On Friday, 7 May 2021 at 15:51:39 UTC, Steven Schveighoffer wrote:
>> But an enum with base string type can be passed as a string.
> 
> "Can be passed as a" is not the same as "is a". There's a conversion involved.

But that's the intention of the function. format doesn't care what the expression really is, it wants some type of string.

How do you say "I want to accept something that's a string, but I want it as a string please"

> For better or for worse, D templates do not participate in conversion and we shouldn't pretend that they do. This is often times very useful - you don't want to lose information in many templates. But there's other times when that information doesn't matter and it would be nice it you didn't have to think about it....

e.g. format.

> ...so maybe we should consider changing templates so they can participate at the language level... it would be interesting if the compiler did the conversions BEFORE instantiating any template. Then it can reuse the instances more easily too. I think it actually does for const params for example, but it could do more.

Interesting idea!

> 
>> User: "OK, but when should I use representation? I already pass it around as a string and it works fine. Why can't phobos comprehend that, when the language has no problems with it?"
> 
> But the language DOES have problems with it for certain types of functions. Phobos is trying to deny that reality.

What I mean is, I can write:

void foo(string s);

and it works for enums that are string-based. Why doesn't format work with that same principle? The answer is because there isn't a good way to do it.

-Steve
May 07, 2021
On 5/7/21 1:05 PM, Steven Schveighoffer wrote:
> I think the problem here is that the language doesn't give you a good way to express that. So we rely on template constraints that both can't exactly express that intention, and where the approximations create various template instantiations that cause strange problems (i.e. if you accept an enum that converts to string, it's still an enum inside the template). Whereas the language

I forgot to finish this thought, got interrupted.

Whereas the language (with non-template parameters) does the matching and conversion simultaneously without needing special cases.

-Steve