No we should not support enum types derived from strings

deadalnix deadalnix at gmail.com
Wed May 12 23:08:24 UTC 2021


On Wednesday, 12 May 2021 at 22:00:57 UTC, Paul Backus wrote:
> 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.
>

Thanks.

> 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.,
>
> [...]
>
> 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

That is true, and there are definitively cases where it is 
unavoidable.

However, I don't think format fits that bill, because format does 
expect a string, not any random type. Where I'm getting at is a 
bit complicated to express clearly, because types are effectively 
also "values" that you can pass around at compile time, but let 
me try.

We should reasonably expect the LSP to work when what is passed 
down is the value of the enum, but not when it it's type - which, 
in fact, isn't too surprising because the type itself isn't 
subject to the LSP.

Consider:

class A{}
class B : A {}

void foo(A a); // We should expect the LSP to hold true here, 
because the value is the only argument passed down to foo.
void bar(T)(T t); // There is no expectation that foo(new A) and 
foo(new B) behave consistently, because not only the value is 
passed down, but also the type.

While we expect passing down the value to respect the LSP, no 
such expectation can exist for the type. So in the second 
exemple, while we expect the runtime parameter `t` to conform to 
the LSP, we do not expect the compile time parameter `T` to do 
so. However, if we do not change the value of `T` but pass a B 
down to `t`, then we should get back to a situation where the LSP 
is respected.

For instance:

bar!A(new B()); // We expect this to be well behaved when it 
comes to the LSP, vs say bar(new A()) because the only change 
happened to the value parameter, which is supposed to uphold the 
LSP.

So far, so good, I don't think this is too controversial, even 
though it is confusing to express that concept clearly.

Now, with enum string, there is an interesting twist, because 
they can be passed at compile time too. in theory, that should 
not change anything when it comes to the LSP, but in practice, it 
seems like it does, which is IMO where the root of the problem is.

Consider:

string format(string S, A...)(A args);

While S is a compile time parameter, it is not a type parameter, 
but a value parameter. In that case, it is expected as per the 
LSP that I can pass down string, or any subtype of strings as the 
first compile time parameter of format, and this ought to work as 
expected.



More information about the Digitalmars-d mailing list