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