Generics

For Generic Programming

The existence of Strong Types may look very restrictive (and it is, on purpose) but it gets necessary to have a way to generalize certain processes and structures for groups of data.

This generalization is (unsurprisingly) called generics. It is programmed with the type being a parameter, and it gets instantiated when compiled.

Similar to C++ templates in concept an purpose. Both get handled at compile time. That means that code is generated, unlike Java (who resolves while running).

But Ada requires initialization of the generic unit explicitly, unlike C++ (a language that never ceases to amaze me, and never in a good way). There is a con to Ada: Meta-Programming is really hard to replicate.

To be honest, my opinion is that Meta Programming is little more than an amusement that provides no actual value as it is presented nowadays. If we find it is of any use, I'm sure Ada will catch up in some version of the language.

But wait, there's more!

Somehow this sounds impossible, but they incorporated strong typing into generics.

Wait, WHAT?!?!

Yes! You can specify properties of the type without knowing what type it is.

Nice!

If the type is a valid candidate, it will compile. If not, you will recive a clear and nice error message (unlike C++. Who knows what can happen or what would be the error message even like?)

Here is an example:


generic
  type Element is private;
  with function "<"(Left, Right: Element) return Boolean is <>;
package Sorters is
  type Element_Array is array(Natural range <>) of Element;
  procedure Quick_Sort( List: in out Element_Array);
end Sorters;

Notice the header of the specification.

In it we specify that it has to be able to be private. Not that it has to be. A limited type is more restrictive, so it is not a valid candidate type. A Public type is more generic, so it can apply.

It also needs from the user that it provides a < function that accepts two of the type, and returns a Boolean. It does not need to be an operator, but any procedure or function can be provided or demanded, they just need to share profile (in/out parameters' properties).

It feels like a contract, right? You give me a type and a function, and I will provide you with code for it. So cool.


The box symbol <> works as a place holder. If the compiler can fill the function automatically, it will. It might be, for example, the standard < for the integer type if that's the type.

Inside the generic the conditions will be assumed, and the code will not compile outside the parameters of the contract.


The generic can be used as follows:


with Sorters;

procedure Sort_Numbers is
  package N_Sorter is new Sorters(   --This builds a package
    Element => Integer,
    "<" => Standard."<");
  Data: N_Sorter .Element_Array;
begin
  Set(Data);
  N_Sorter .Quick_Sort(Data);  
     --Please, note that the dot notation accepts separators
end Example;

I'm pretty sure you noted how ugly the line

"<" => Standard."<");

looks. It's a better practice to name your functions and procedures than overloading, but to each his taste.

Also, as we used the <> symbol, we could have omitted it completely.


The name N_Sorter is given on the spot. By making a package we can then use it as any other package. You can see it as instantiating an Object in C++/Java. But you can use a use clause and forget about it and use it as procedural programming.

Processes (procedures and functions) can also be defined as generic units.