Thesis: Not to be generic
At first, I didn't want to include generics in LL. This is mainly for two reasons.
First, because it is really hard to conceive and understand a generic algorithm. This goes at contrary of the Wish N°5 – Simple and freed of experts. In my professional life, I have often seen developers using generics libraries, but I have never seen anyone create a significant piece of software with generics…
These use are often limited to containment systems (a container containing contained whose type can vary).
Moreover, this feature is not required to be able to make software. Many languages do not support this way of making programs. Think that the first versions of Java had not generics and that thousands of software have been made like that.
In fact, in an oo world, using the genericity can be seen as a writing shortcut. For instance, in Java instead of making a ArrayList, you can alternatively create a new class ArrayListString with the same members internally using an ArrayList.
So why to include a complex feature in a language if it is not required?
There are other considerations that lead to not having the genericity in LL. They are put jumbled below.
For instance, the “need cast” problem from a more general type to a more specific one can be managed by auto casting.
Below is an example of how auto-casting acts.
In Java with an explicit cast:
String myString = (String) myArrayList.get(i);
In LL with auto-casting in my string of type String:
my string $: my array list item: i.
The compiler sees that the result of my array list item: i goes in a variable of type String, so it can manage automatically the cast.
One must notice that genericity is not an object Oriented principle. So there is no strong conceptual reason to enforce to have it in LL while ISA just relies on the oo paradigm.
Requiring some operations for a generic behavior to be usable could be done thru a required interface ((like (I)Comparator in Java and C#). In other words, the regular oo way to provide the genericity and type independence is the polymorphism.
The genericity is primarily a compile time feature that may result in creating as much as compiled types that there are declared generic types in the code. This might lead to a wide overload of the compiled code.
Generics also imply a more complex compiler and run time, things which also have some cost.
The invariant feature allows to express constraints that can replace some feature of generics like enforcing something to be of a given subtype.
There is also the typing crunch reducing the type of the object to one general type. With our multiple and dynamic inheritance policy this is something that could lead to have conditions on type and to cast on object. So, something we try to avoid with generics…
There are mainly 3 ways to reuse software pieces (Design Patterns: Elements of Reusable Object-Oriented Software) :
All of them have flaws: Static inheritance make software very rigid. Delegation implies to manage relationships between objects, which are being often in fact facets of the same conceptual object, that is changing all long its life cycle. Generics are not linked to oo paradigm and cannot be changed at run time. The LL approach is to be both dynamic and static. Dynamic inheritance means that you can add or remove a type to an object. An Employee become a director? No problem, just let the same object inherit in addition and at runtime of the class Director. |
The literature also points out common the genericity difficulties (in particular around constraints support), theirs advantages and also the implementation limits by the languages:
- The limits of Java Generics
Bruce Eckel – 2004 - A Comparative Study of Language Support for Generic Programming
Ronald Garcia, Jaakko J¨arvi, Andrew Lumsdaine, Jeremy Siek, Jeremiah Willcock – 2003 - Language Support for Generic Programming in Object-Oriented Languages: Design Challenges
Julia Belyakova – 2016
Antitheses: To be generic
Well, this list of disadvantages done, why are the generics supported by so many languages? Certainly for some good reasons. If this is not the case, the industry would certainly have neglected it, as it is the case with functional programming.
So what are the major pros of the genericity (parametric polymorphism)?
The first and most important one is Type checking.
This means that the type check can be performed at compile time instead of at run time. This makes software much more sure, because the type checking is systematized, while at run time, an exploding number of tests have to be done to reach the same result.
The second major advantage is Code reuse. This property is important because the same structure and algorithm can apply to several different types. This promote software quality and reliability.
Finally, for the programmers it simplifies the code by avoiding casting and/or verbose encapsulation.
Synthesis: Not be too much generic
The advantage above needs to be mitigated. The code reuse for instance can also be done by pure oo technique, as this is one of its main aim.
Verbosity and explicit casting can be avoided thru auto-casting as seen at the thesis part.
The only remaining really strong argument is type checking.
When affecting a part of an expression, there are two cases depending of the multiplicity of the part.
- If the minimum multiplicity is 0, the cast failure results in a Root universal type to that part. The optional situation do not make problem of type checking.
NB: The Root type propagate and this can be checked at compile time. - If the minimum multiplicity is >0, the cast failure inevitably throw an exception.
So there is no way to ensure type robustness in this case that having generics, even if LL has a Root universal type.
To help decide, let's see what some authors say about genericity:
- From Designing Programming Languages for Reliability (Harry H. Porter III – 2001)
one can conclude of this article that parametrized type promotes for code-reuse in particular for container types. - From Gang of Four, Design Patterns (Chapter 1)
one can says that the authors of Design Patterns note that this technique, especially when combined with delegation, is very powerful, however, Dynamic, highly parameterized software is harder to understand than more static software. - Tony Cox: “Premature Abstraction is the Root of All Evil” told us E. Deloget in gathering opinions of several major experienced practitioners. And more precisely here that to follow the YAGNI principle applies almost always to genericity. This because, even if meta-programming is very powerful (irreplaceable) of course when applied accurately, it drives to unnecessarily complex software for usual development.
To conclude:
1/ Generics promote reliability, and LL, with its central position in ISA, cannot avoid it.
2/ There is not consensus on how to support generics, from no support (like Python and its Duck typing mechanism) to large but never with full support (Cf. Language Support for Generic Programming in Object-Oriented Languages: Design Challenges – Julia Belyakova – 2016).
3/ That in real world applications (those written with generic languages), generics are quite often used, but are rather never written by programmers because of their complexity.
Option: LL offers a basic support of generics only where they have proven to be very useful: on containers |
This means a support of the genericity only on built-in types (Elements) that are containers either in space (Array, associative Table) or in time (Stream). These built-in types are not extendable and do not support complex generics features like constraints. With just some built-in Elements being generic, both the compiler and the runtime remain simple and efficient.
To simplify the language for beginners and to not force their usage, a default type Object is provided on theses generics types. More advanced users can build classes with a class type as an argument of constructors, say T, and put arguments in the members. This just require to add an invariant to ensure the T in the constructors and in the members are of the same Type.