The first level of modularity in a programming language is the 'if' control. Everyday programs reveal that the the 'if' is very often used the following way:

if status_variable=case1
  bloc1a
eif status_variable=case2
  bloc1b
else
  bloc1c
...
if status_variable=case1
  bloc2a
eif status_variable=case2
  bloc2b
else
  bloc2c

which means that there is an external event which has been summarized in 'status_variable', and depending on this initial conclusion, we are going to execute different code sequences at several steps of the algorithm.

Drivers / API / generic functions is an elegant and efficient way to rewrite such a common piece of code ...... and when software design and efficiency call for the same solution, it is a must.


You probably have noticed that a Pliant generic method is roughly the same as a C++ virtual method. The key difference is that with C++, you can extend one way, adding new cases (new classes), but you can't extend the other way, adding new methods, because all the virtual methods prototypes must stand in the generic type (class) prototype. In other words, in C++, the potential generic usages of a type is frozen when the type is declared, not when it is used. So for basic types such as integers, no is available.
Pliant is much more ... pliant: you can add generic methods to existing types whenever you need it.

Important: You have to keep in mind this key difference between Pliant genericity and C++ one:
In C++, genericity applyes at 'class' level. In other words, if a class contains a generic method, then all it's instance will be generical. On the other hand, if an existing class is not generical, you have no way to add generic methods to it.
In Pliant, genericity applyes at 'instance' level. You can define generic methods on any existing data type, but you can apply them only to true objects (global variables and objects allocated by 'new' function).


This is my point of view on object programming:
object programming = generic functions + heritage

Now, still according to my experiment, the same mistake has been repeated several times in computer tools evolution ...

... are all the same evil features: allow to produce applications that often get completely unmaintainable, even if sometime very elegant. The main reason is that the code is hard to read. Hard to read means that it is hard to find where each operation required to solve the problem stands.
So Pliant has no heritage mechanism.

However, remember that if you really like it, Pliant meta programming power allow you to create you own heritage notion.

Before introducing Pliant generic functions, we must define Pliant generic type notion. In the initial example, we would introduce one Pliant generic type which is the glue code, and one Pliant instance type of that generic type for each case.

A Pliant generic type is an ordinary Pliant type, it simply contains a list of instance types defined using maybe method of the Type type.

generic_type maybe possible_type
  arg_rw Type generic_type ; arg Type possible_type

Now a generic method is a method (or function) applied on the generic type, marked as generic using the generic keyword, that will have a different implementation for each of the instance types.

Let's look at an example:

module "/pliant/language/compiler.pli"

type Number
  void
Number maybe Int
Number maybe Intn
Number maybe Float

method n show -> s
  arg Number n ; arg Str s
  generic
  s := "?"

method i show -> s
  arg Int i ; arg Str s
  s := string i

gvar Link:Int i :> new Int
i := 5
gvar Link:Number n :> i
console n:show eol

'Number' is the generic type.
As you can expect, the program would display 5 at the end.

Please remember: Generic methods can be called only on true Pliant objects (allocated by 'new'), not on variables allocated on the stack or on objects fields.


As expressed at the beginning of this document, for each generic method, there is a pointer in each of the generic and instances types to the corresponding function, so a function index must be allocated to the generic method that will define which pointer in the type pointers array is dedicated to the generic method. The generic level parameter is intended to allow you to optimize the usage of these indices.

Several generic types may share the same index for virtual functions if they have the same generic level and don't have any instance type in common when the generic function is defined.

So if no instance types of a generic type is shared with any other instance of another generic type, which is the most common situation, then don't use generic level parameter; it will use the default value 1, and everything will be fine.
Generic level 0 is used for the 'Universal' (any type which is not a pointer or an arrow is an instance of 'Universal' data type).