Concept


Polymorphism in programming means the ability to present the same interface for multiple different underlying forms. In object oriented programming it's the ability to have the same interface or abstract base class for multiple subclasses, and have the methods of the abstract class or interface behave differently depending on the subclass implementing it.

In polymophism the base class defines a set of methods and allows subclasses to provide the implementation for these methods. This is done by defining the methods as function pointers, which the subclass can override by updating these pointers with their own functions.

Using Polymorphism


To use polymorphisms with classes you should define functions as pointers, this is done by adding the keyword `as_ptr` to the command `handler` (in a similar way to keyword `virtual` in C++).
Assume we have the following class:

class A {
  handler this.set(i: Int) as_ptr { ... }
}

In the above definition, the compiler will add to the class `A` a pointer to the function `set`. This enables the classes derived from this class to override this function by changing the value of the pointer, for example:

class B {
  @injection def a: A;
  handler this.set(i: Int) set_ptr { ... }
}

In this definition, the class `B` inherits from class `A` and change the pointer of function `set` using the keyword `set_ptr` to a definition for `B`.
Now if there is a function that takes a reference or a pointer to `A` you can give it a reference to `B` and the function will use it as if it were a pointer to `A`, with `set` function that belongs to `B`.

Example 1:

We define a class called `Country` with 3 functions their body is not specified yet.
Then we define a class called `Canada` and a class called `Syria`, both of which inherit from `Country` and each of them has their own implementation of the abstract functions.
Then we define a global function `printCountryInfo` that calls all the methods from the object we pass to it.
Finally, we create an object from class `Canada` and another one from class `Syria`, and pass each of them to `printCountryInfo` function.

import "Srl/Console.alusus"
use Srl.Console;
// define a class that represents a country.
class Country {
  // function used to print the name of the country.
  handler this.name() as_ptr;
  // function used to print the capital name of the country.
  handler this.capital() as_ptr;
  // function used to print the language of the country.
  handler this.language() as_ptr;
}
// define a class that represents a specific country which is Canada.
class Canada {
  // inherits class `Country`.
  @injection def country: Country;
  // define the functions in the class `Country` with a special definition.
  handler (this:Country).name() set_ptr {
    print("Country: Canada\n");
  }
  handler (this:Country).capital() set_ptr {
    print("Capital: Ottawa\n");
  }
  handler (this:Country).language() set_ptr {
    print("Language: English\n");
  }
}
// define another class that represents a specific country which is Syria.
class Syria {
  @injection def country: Country;
  handler (this:Country).name() set_ptr {
    print("Country: Syria\n");
  }
  handler (this:Country).capital() set_ptr {
    print("Capital: Damascus\n");
  }
  handler (this:Country).language() set_ptr {
    print("Language: Arabic\n");
  }
}

// a function that takes a reference to `Country` then it uses it to access the functions.
function printCountryInfo(re:ref[Country]) {
  re.name(); // print the country name.
  re.capital(); // capital name.
  re.language(); // language.
  print("********************\n");
}

def obj1: Canada;
// now we will pass the object to `printCountryInfo` function to print this country information.
printCountryInfo(obj1);

// we will repeat the same thing but with an object from `Syria` class.
def obj2: Syria;
printCountryInfo(obj2);
/*
Country: Canada
Capital: Ottawa
Language: English
********************
Country: Syria
Capital: Damascus
Language: Arabic
********************
*/

Thus we wrote a simple function that use polymorphism.

Example 2:

We will use the previous example but with dynamic arrays.

import "Srl/Console.alusus"
import "Srl/Array.alusus";
use Srl;

class Country {
  handler this.name() as_ptr;
  handler this.capital() as_ptr;
  handler this.language() as_ptr;
}

class Canada {
  @injection def country: Country;
  handler (this:Country).name() set_ptr {
    Console.print("Country: Canada\n");
  }
  handler (this:Country).capital() set_ptr {
    Console.print("Capital: Ottawa\n");
  }
  handler (this:Country).language() set_ptr {
    Console.print("Language: English\n");
  }
}

class Syria {
  @injection def country: Country;
  handler (this:Country).name() set_ptr {
    Console.print("Country: Syria\n");
  }
  handler (this:Country).capital() set_ptr {
    Console.print("Capital: Damascus\n");
  }
  handler (this:Country).language() set_ptr {
    Console.print("Language: Arabic\n");
  }
}

function printCountryInfo(re:ref[Country]) {
  re.name()
  re.capital()
  re.language()
  Console.print("********************\n")
}
// define a dynamic array each item in it is a reference to `Country`.
def countries: Array[ref[Country]];
def obj1: Canada;
def obj2: Syria;
def i: int;
countries.add(obj1); // add the first object to the array.
countries.add(obj2); // add the second object.
// loop through array items and pass them to `printCountryInfo` function.
for i=0,i < countries.getLength(), i++ {
  printCountryInfo(countries(i));
}
/*
Country: Canada
Capital: Ottawa
Language: English
********************
Country: Syria
Capital: Damascus
Language: Arabic
********************
*/