No we should not support enum types derived from strings

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Tue May 11 15:33:45 UTC 2021


On 5/10/21 5:55 PM, deadalnix wrote:
> On Monday, 10 May 2021 at 13:30:52 UTC, Paul Backus wrote:
>> popFront doesn't return a value, it mutates. So `r` before popFront 
>> and `r` after popFront must be the same type, because they are the 
>> same variable.
>>
>> If popFront for a string enum is `r = r[1 .. $]`, and typeof(r[1 .. 
>> $]) != typeof(r), then it doesn't work, and string enums can't be 
>> ranges (from which it follows that they are not Liskov-substitutable 
>> for strings).
> 
> r = r[1 .. $] is an error unless r actually is a string. You cannot 
> mutate an enum value and have it stay an enum.
> 
> If you think that invalidate the LSP, I'm afraid there is a big 
> misunderstanding about the LSP. Not all operation on a subtype have to 
> return said subtype. It is made clearer if you consider the slicing 
> operationa s a member function on an object instead - as I seems classes 
> and inheritance is the only way OPP is understood these days.
> 
> class A {
>     A slice(int start, int end) { ... }
> }
> 
> class B : A {}
> 
> Where is it implied that B's version of the slice operation must return 
> an A?

If we move the goalposts we can with certain ease create the illusion 
that a lot of things are possible and even easy. This works very well in 
forum discussions where all needed is eloquence and the perseverance to 
answer every post with one that just slightly moves the discussion 
around so it appears to have answers to every objection and have the 
last word on any topic. This is exactly what happens here - half of your 
points contradict the other half, but never in the same post and the 
appearance is you seem to have easy answers to everything.

In the initial days of ranges we actually considered that popFront() 
would be actually tail() that returns by value. So instead of today's 
form (given a range r):

for (; !r.empty; r.popFront) {
    ... use r.front ...
}

we'd have had:

for (; !r.empty; r = r.tail) {
    ... use r.front ...
}

This doesn't change things much (and wouldn't improve the situation with 
enums) but does open up the possibility - what if r.tail() actually 
returns a type different from r?

In all interesting cases that means r = r.tail wouldn't work anymore, 
which complicates range algorithms A LOT. They'd need to use recursion 
instead of iteration:

void someRangeFunction(R)(R range) {
     if (range.empty) {
         ... empty case ...
     } else {
         ... do some work for r.front ...
         return someRangeFunction(r.tail);
     }
}

(I should note that that's actually of interest for immutable ranges, 
for the simple reason they aren't assignable.)

At any rate, we decided this would complicate everything in Phobos way 
too much (and I think that was a correct prediction) so we chose to have 
popFront() mutate the current range.



More information about the Digitalmars-d mailing list