|title "Generic functions"
header "Generic functions: one solution among several"
header "Drivers / API / generic functions : all the same"
[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:]
[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 ]
[because the blocs are grouped the other way around, i mean everything about case1, then everything about case2, and so on.]
[ and ]
[because the time to select the right piece of code in each switch is constant instead of being linear with the number of possible values for the status variable.]
[ way to rewrite such a common piece of code ...]
[... and when software design and efficiency call for the same solution, it is a must.]
item [So the basic idea is that the status variable will contain pointers to the code that must be executed at each step of the algorithm depending on the status variable. Such a solution is often found in device drivers. The API (application programming interface) is the visible part of the device driver.]
item [Another solution is generic functions: the pointers to the various pieces of code are stored not in the status variable but in the type of this variable, and so the variable needs only to contain a pointer to it's own type. The key advantage of this second solution introduced with object programming languages is that the interface glue code gets smaller.]
header "Pliant generic methods key difference"
[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.[lf]]
[Pliant is much more ... pliant: you can add generic methods to existing types whenever you need it.]
highlight "Important: "; [You have to keep in mind this key difference between Pliant genericity and C++ one:] ; eol
[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.] ; eol
[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:[lf]]
fixed [object programming = generic functions + heritage[lf]]
[Now, still according to my experiment, the same mistake has been repeated several times in computer tools evolution ...]
item [logical programming]
item [object programming heritage]
item [spreadsheets sophisticated macros]
[... 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.[lf]]
[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.]
header "Using Pliant generic functions"
[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] ; fixed [ 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] ; fixed [ generic ] ; [keyword, that will have a different implementation for each of the instance types.]
[Let's look at an example:]
| gvar Link:Int i :> new Int
i := 5
gvar Link:Number n :> i
console n:show eol
['Number' is the generic type.[lf]]
[As you can expect, the program would display 5 at the end.]
highlight "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.]
header "Understanding the generic level parameter"
[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.[lf]]
[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.[lf]]
[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.[lf]]
[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).]