No we should not support enum types derived from strings

Paul Backus snarwin at gmail.com
Wed May 12 22:00:57 UTC 2021


On Monday, 10 May 2021 at 21:55:54 UTC, deadalnix wrote:
> 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? Nowhere, the LSP absolutely doesn't mandate 
> that. It mandate that you can pass a B to something that 
> expects an A, and that thing will behave the way you'd expect.
>
> And it does!
>
> If your code needs an A, then you mark it as accepting an A as 
> input. If I have a B and want to pass it to your code, I can 
> too, transparently. You do not need to even know about the 
> existence of B when your wrote your code. This is what the LSP 
> is at its core.
>
> Back to our string example, the code should accept string (A), 
> with zero knowledge of the existence of any enum string (B). 
> You should be able to pass a B to that code and have everything 
> work as expected.

I concede the points that enum strings do not violate the LSP, 
and that they are subtypes of string. You're right, and I was 
wrong.

The point I should have made is that, at least in D, the LSP is 
not universal. There are situations where it simply does not 
apply. In particular, it does not guarantee that a substitution 
which changes the arguments used to instantiate a template will 
succeed; e.g.,

     class A { int x; }
     class B : A { int y; }

     void example(T)(T obj) {
         static assert(!__traits(hasMember, T, "y"));
     }

`example(new A)` will compile, but `example(new B)` will 
not--because they are not actually calling the same function. One 
calls `example!A` and the other calls `example!B`. This is an 
unavoidable consequence of the expressive power of D's templates: 
without specific knowledge about `example`'s implementation, we 
cannot guarantee anything about the relationship between 
`example!A` and `example!B`.

All of which is to say, the fact that you can pass a string as an 
argument to a template does not *necessarily* imply that you can 
pass an enum string as an argument to the same template. That 
`format` handles them differently does not "fly in the face of 
Liskov's substitution principle" [1], any more than my example 
above does.

[1] 
https://forum.dlang.org/post/fnibsejuozasspsggxie@forum.dlang.org


More information about the Digitalmars-d mailing list