Nessa does not allow inheritance-based polymorphism as in most programming languages. Instead, generics are entirely based on **bounded template substitution**. In order to use this, we need to define **interfaces** and **implement** them for out types. In this section we will define the syntax and semantics of this mechanism. ## About interfaces An **interface** can be defined as a set of function and operation signatures that may or may not be fulfilled by a target type which is marked as `Self`. We say that a type `T` fulfills the interface if every function and operation definition inside the interface exists when we substitute `Self` by `T`. Also, when a type fulfills an interface, you can **implement** it, which means that we mark thye type so that the interpreter knows it and we can make use of bounded substitution on that interface with that type. Implementations can also be **parametric**. ## Syntax We can define an interface using the following syntax: ``` // Simple interface Example { fn example_1(v: Self); fn example_2(...) -> Type; [...] op prefix_op (v: TypeV) -> Type; [...] op (v: TypeV) postfix_op -> Type; [...] op (a: TypeA) binary_op (b: TypeB) -> Type; [...] op (a: TypeA) nary_open args nary_close -> Type; } // With generics interface Example { fn example_1(v: Self, n : 'T); fn example_2(...) -> Type; [...] op prefix_op (v: 'T) -> Type; [...] op (v: 'T) postfix_op -> Type; [...] op (a: TypeA) binary_op (b: 'T) -> Type; [...] op (a: 'T) nary_open args nary_close -> Type; } ``` Then we can implement it using the following syntax: ``` // Simple implement Example for Type; // With generics implement Example for Type; // Generic implementation (define for all arrays) implement Example for Array<'T>; ``` If `Type` does not fulfill `Example`, the interpreter will raise an error. ## Semantics The best way to understand how this works is with an example. Imagine that you want to create a function that takes an argument of a type that can be converted to `Int`. You could start by writing this: ``` fn test(n: Int) -> Int { [...] } ``` This is problematic because you would have to convert everything on call time. To avoid this you could write something like this: ``` // An overload for each supported type fn convert(n: Int) -> Int { return n.deref(); } fn convert(n: Float) -> Int { return n.floor(); } // Use generics fn test(n: 'T) -> Int { let converted: Int = convert(n.deref()); [...] } ``` The problem with this approach is that you rely on the `convert` function being defined for `'T` and that might not be the case. Even worse, it could be defined with wrong types and cause difficult to debug errors. In order to avoid all this, you can use an interface and **bounded substitution**: ``` // Define interface interface Convertible { fn convert(n: Self) -> Int; } // An overload for each supported type fn convert(n: Int) -> Int { return n.deref(); } fn convert(n: Float) -> Int { return n.floor(); } // Explicit implementations implement Convertible for Int; implement Convertible for Float; // Use bounded generics fn test(n: 'T [Convertible]) -> Int { let converted: Int = convert(n.deref()); [...] } ```