Why does D not have generics?

Q. Schroll qs.il.paperinik at gmail.com
Mon Jan 11 18:23:20 UTC 2021


I don't want to be mistaken. I know what the differences of 
generics (cf. Java, C#) are and what templates (cf. D, C++) are. 
Maybe my question has obvious answers and I just don't see them.

The only programming language I know of that has both, templates 
and generics, is C++/CLI and ones closely related.

I wonder, why D has classes similar to C# and Java, but does not 
have generics. Templates can be used in some use cases, templates 
have their specific use cases (like DbI). What seems to never 
mentioned is that generics have their specific use cases, too.

What I may be missing, is how Java-esce generics can be 
implemented using D's templates (without much effort). What I'd 
expect from generics is this:

A generic class or interface states its requirements (base 
classes, interfaces, [never seen in the wild:] subclasses, ...) 
to its type parameters exactly. Everything that is part of the 
implementation is checked when the generic aggregate is defined. 
It is not necessary to instantiate a rainbow of types to be sure 
the implementation is valid for every kind of type parameter the 
programmer intends to support.

Generics allow for implicit conversion by covariance and 
contravariance. (C# [1]) Generics allow for reducing an interface 
to its covariant or contravariant part-interface (that is a 
supertype of the interface, really). (Java [2, 3])

Generics are really great for specifying the expectations of your 
type parameters' requirements and letting the compiler make sure 
the requirements suffice to allow for the stuff you want to work.
Using templates, the compiler checks requirements only for 
specific instances. It might be that the requirements are 
insufficient, but because no test tried the potentially very 
specific type argument, it will be unrecognized.

Also, one feature D doesn't have, is expressing the precise union 
or intersection of interfaces: Say you have two interfaces I and 
J. The interface (I & J) has all the methods specified by any of 
them. A class that implements I and J automatically implements (I 
& J). The interface (I | J) has all the methods specified by both 
of them. A class that implements I or implements J automatically 
implements (I | J). If you e.g. iterate an (I | J)[] object, you 
can call any method required by both interfaces. (Typescript [4])
It might be hard or even impossible to implement this using 
vtables and stuff.

In D, one can easily create templates intersectInterface(Is...) 
and unionInterface(Is...) that basically do that.

It could very well be that D doesn't have them because they have 
to be implemented and maintained, and the cost/benefit ratio 
wasn't good enough. Maybe they can be implemented in a library 
rather easily. I know that me failing to do that doesn't prove 
it's impossible or even hard.

[1] 
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/variance-in-generic-interfaces
[2] 
https://docs.oracle.com/javase/tutorial/java/generics/lowerBounded.html
[3] 
https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html
[4] 
https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html?ref=hackernoon.com


More information about the Digitalmars-d mailing list