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