Generic Types
Generic types allow you to create Types or methods that can work with a variety of data types, while providing type checking to ensure type consistency.
By using generic types, you can write code that is more reusable and flexible. For example, instead of creating separate versions of a Type or method for different data types, you can define a single implementation that works with a placeholder Type and let the client specify the actual Type when using the code.
Generic types
You have seen how an array instances' type() method returned different values depending on the array's element Type. This is because Array is generic: Array<E>.
Note: By convention, single (or very few) uppercase letters are used for generic variables.
E is the generic variable that represents the Type of the array's elements. This allows you to define Array.c3typ one time and have it work with elements of any Type.
Generic Types borrow similar concepts from languages such as Java or TypeScript. These Types are partly declared in terms of generic variables, which are bound in concrete sub-Types. A commonly used Type is Pair, which has two generic variables.
You can create a named Type that binds the generic variables of its mixins:
type TaggedComplexArray mixes Pair<string, [Complex]>Pair is generic because it has two unbound generic variables.
TaggedComplexArray is not generic because its declaration binds the generic variables of its mixin, and it does not introduce any of its own. For example, intermediate bound Types are available for common cases of Timeseries, where key ValueTypes have extra methods.
You can use the mixing keyword to declare a method in a parent Type, and refer to the actual Type it corresponds to in a sub-Type.
For instance, to declare mapTo in Collection, you can use mixing Collection<E>. Otherwise, if you declared the return value as Collection<E>, it would not be clear whether that was just some collection, or specifically the collection sub-Type on which mapTo is called.
By using mixing Collection<E>, you are stating that it is the latter. For arrays, mapTo returns an Array<E> and for sets it returns a Set<E>.
This is doubly useful because if a method is declared abstract in a mixin, it is assumed that each immediate sub-Type provides an implementation. In other words, you do not have to re-declare the method in the sub-Type (it is implied by the mixin). If you do need to declare the method, you can use ~ for the Type and just add whatever modifiers you need such as cached or a language claim.