One of the most important aspects of the Nessa type system are **templates**. These allow you to pass types as parameters in order to create other types. This is mainly used in function and class definitions to prevent code repetition. In this section, we will only explain the basic syntax and semantics of templates. ## Introduction A **template** can be defined as a placeholder for another type to go into. They allow the programmer to write typed structures such as "lists of *T*", where *T* is a type with certain properties without the need of rewriting the class for each possible *T*. The implementation can be a litle more involved, but it pays off as a library writer. ## Syntax Type templates are specified in the context of **structural types**, **classes**, **functions** and **operations**. Each of these has their own syntax and diving into the specifics would be outside the scope of the type system, but we will talk about the syntax when templates are already defined. Templates are always represented with an single quote character before an alphanumerical identifier that follows the same rules as class names. Some examples of valid template names would be `'Test` or `'Parameter_1`. Names such as `'0123` or names with non-ASCII characters are not allowed. Now, this is the way to reference a template that has already been defined, but when you have class that **depends** on one or more templates (i.e. a **parametric class**), you also have to specify the type parameters when referring to the class. For example, you cannot refer to the `Array` class without specifying the type of data that goes inside the array as the first (and only in this case) type parameter. These parameters are specified the same way as in languages such as C++, with angle brackets. These would be some examples: ``` Array // Array of Ints Array // Array of either Floats or Strings Array<*> // Array of anything Array<'T> // Array of 'T (only if 'T is in scope) ``` Of course, we can define a class with an arbitrary amount of type parameters, but Nessa does not include any with more than one by default. If we had an hypotetical class called `HashMap` that had two parameters (the type of the key and the type of the value), we could refer to it like this: ``` HashMap // HashMap from Ints to Ints HashMap // HashMap from Strings to either Floats or Strings HashMap<*, *> // HashMap from anything to anything HashMap // HashMap from Bool to 'T (only if 'T is in scope) ``` ## Semantics Like we said before, a template is not exactly a type, but a placeholder for a type that will be substituted by a proper type later. This is done to prevent manual code repetition and allow the creation of more general code. How this works is pretty simple. Every time you create a **parametric class** and refer to it anywhere in the code, the interpreter will substitute the parameters automatically for the ones given inside the angle brackets. This can be essentially understood as creating a new class for each distinct instance of a parametric class. This mechanism works exactly the same way for **parametric functions**, **structural types** and **operations**. Of course, the fact that you substitute a type does not mean that the code magically works. In fact, it might fail to compile if you try to call a function overload that does not exist. The main way to avoid this is **bounded substitution** via **interfaces**. ## Bounded substitution > **Note:** this works as of now, but syntax will probably change in future releases This mechanism allows the programmer to specify constraints while defining a parametric type instance. One example of why you would want to do this is that `HashMap` class we discussed earlier. As you might know, a *HashMap* structure requires keys to be **hashable** because of how data is stored inside for fast retrieval, but not all types might be hashable and you would also want to user to know that they have to implement a hashing function for a custom type before they can put it as a *HashMap* key. This is what **interfaces** do, they allow you to define APIs and assert whether or not a type follows it or not. If you define an interface called `Hashable` that checks whether or not the type that implements it (called `Self`) has a function called `hash` that takes a `Self` and returns an `Int`, you can use it as a **constraint** for template substitution. This woult be done simply by changing the references to `'T` inside the class definition to `'T [Hashable]`, which translates to *a T which is Hashable*. You can put as many constraints as you want separated by commas and the constraints can also be parametric, following the same syntax rules as types.