Design rationale of these Javascript functions
Content normalization
(paradigm).
These functions use a style compatible with
purely functional programming and hence
declarative programming.
Indeed, this has many advantages. It eases the reuse, analysis (proof, ...) and
parallelization of functions, their conversion to other programming languages
or their use along with functions from other programming paradigms, including
logic programming.
More precisely, one of the goals is to help programmers design applications
that let their end users parameter them as much as possible (including their
functions and interfaces).
This implies specifying the data and functions (processes, interfaces, ...)
of these applications in a declarative way in a
knowledge-based system
and its ontology
and, when needed, help each end user specialize, adapt or complement
these specifications. For specifying the data an interfaces, the use of a
knowledge representation language (KRL) is then necessary.
The processes have to be specified in a declarative way.
Here are the three possible ways
by decreasing order of easiness for process analysis and parametrization
but increasing order of easiness for specifying them:
i) using a KRL,
ii) logic programming, and
iii) purely functional programming.
These functions support the third way. The interpreted nature of Javascript
help enabling dynamic specification of processes by the end-user.
In practice:
- These functions can be object constructors and most generally follow an
object-oriented style but are not object methods, e.g.,
instead of adding (or trying to add/adapt) the method
toOrdinal to the class Number (which is actually not a genuine
class but a wrapper object, hence not modifiable/specializable),
I have created the function Integer_toOrdinal(/*Integer*/i).
Thus, Integer_toOrdinal(2) returns "2nd".
Similarly, I have created a function Obj_className(/*?*/o) which,
for any object given in parameter (not just an object specializing
the class Object) returns its class/type,
not just for objects specializing the class Object.
This style has several advantages over the use of genuine object methods:
- As above illustrated, this style permits to add (pseudo-)methods to
pseudo-classes (e.g., Integer, wrapper objects, ...) and to the class
Object (adding genuine methods to Object may cause problems in Javascript:
see ...).
- The schema "SomeClass.prototype.someMethod= function (...) {...}"
permits to define genuine methods anywhere (not just within the declaration
of a class) and thus group these definitions with related ones of other
classes but
- in Javascript, unlike functions, class definition/expressions are not
hoisted:
they cannot be used before being defined, which may often be problematic;
- many text editors do not yet recognize this schema as a method definition
and hence do not highlight it in the way they highlight function
definitions (a way that makes the code clearer);
- with the above illustrated style, any person can immediately know which
peudo-method of which class is executed; when genuine methods are called,
this is often not the case; conversely, with the above illustrated
style, the overloading of pseudo-methods can still be done.
- I created and use the pseudo-class Obj which generalizes all other classes or
pseudo-classes : Object, Function, String, Integer, ...
- These functions always return a value, even when they are called with
parameters not conform to their signatures; in other words, exceptions
(error throwing) are not used. When a non atomic object (i.e., string,
array, date, ...) is returned by a function if it encounters no error,
it returns null if it encounters an error.
To ease tests on its return, it also returns null if the object to return
is empty (hence, e.g., instead of "" or []).
To ease translations to languages that do not have the value undefined,
it also returns null instead of it.
To ease translations to languages that do not have the value Infinity
(Number.POSITIVE_INFINITY), it also uses -1 instead of it.
- These functions never modify their parameters except when
- they return it after modifying it,
- it is the first parameter of an object-oriented style function which
make this modification explicit with the keyword "varP" as in
the following example:
function /*Boolean*/ KB_aBooleanFunctionOnThisKB (/*varP KB*/kb, ...)
Such functions are not "pure" functions but can be automatically
converted into ones by i) returning an object composed of their
modified first parameter and their initial return value, or ii) using
monads.
Monads can be used since, in these functions, in accordance with the
goal of letting the user parameter things as much as possible,
such a modified first parameter is either the whole KB or a node of the
KB from which the whole KB can be accessed. The user can then parameter
things (e.g., where and how outputs are done) in a general way
(at the top-level of the KB) or in specific way (e.g., for particular
functions.
- These functions use higher-order functions when this helps make things
(strategies, ...)
first-class citizens,
hence more parametrable or reusable.
Thus, they often do not use the design patterns designed for object-oriented
languages.
- When useful, these functions have many parameters, many of which can be
"default parameters", to avoid a combinatory explosion of similar functions
to define and use (this proliferation of similar functions inevitably leads
to code duplication, confusion and errors).
E.g., the function Object_destsVia has many parameters and
can thus be used for searching objects with respect to various constraints
as well as for testing if an object is linked to another object with respect
to various constraints. Currently, for such searches, the functions
Object_destsVia, AltPaths_add and AltPaths_addFromNodes are the primitives
and "functions to use" proposed by the file kb.js.
Presentation normalization
(programming style:
conventions for program presentation).
The goal of the used style is to make the functions as easy to retrieve,
understand and reuse as possible. This implies
i) making thing as formally explicit as possible (here "formally" means
"usable by a software"), and
ii) using a concise style when this does not conflict with explicitness.
The second point comes from the limited
short-term memory of most
people: functions should be as short as possible for people to see as much
of them without scrolling and thus undest and compare the easily.
If scrolling is necessary, debugging or reuse time is greatly increased.
In practice, these functions
- use an indentation of only 2 spaces (and never tabulations since they are
often not portable accross platforms or tools);
- use lines with a maximum of 99 characters (83 characters if the line does
not include any comment) to avoid line wrapping in small screens
and hence increase readability across tools;
- for the '{' '}' brackets, follow the
Horstmann style
(it is equivalent to the
Allman style
-- alias BSD style -- when the '{' is alone on a line, which is to avoid
since this is a useless loss of space);
- use assigments written like "i= 1;" or "i=1;" never like "i = 1;"
since this wastes space and ressembles too much equality tests ("i==1");
- rather than comments, use long identifier names and error checking tests
with explicit error messages; indeed, comments are only present in the
function definitions (not in its calls) and are lost for checking/analysis
purposes; comments are used when more formal way of representing information
cannot be used in Javascript, e.g., for grouping/organizing functions into
sections and sub-suctions within a file;
- use a space after the function name in its definition, never when in its
calls (this eases grep-like search of function definitions in programs with
many modules);
- use the InterCap/CamelCase style
for identifier and the object-oriented-but-not-object-based above illustrated
style;
- systematically use certain variable names: "r" for (a variable holding ) a
value to return, "i" or "j" for an indice, "lng" for a length, "o1" for a 1st
parameter which is an object (the digit at the end of the name eases the
spotting and search of the uses of the variable), etc;
begin the names of boolean variables by "has", "is", "are", "no", "with" or
"do", and the names of boolean functions by "has" ("is...Of" is avoided for
normalization or harmonization purposes).