Generic Local Objects
Vector 5:3, page 103
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 103
Abstract¶
A proposal is made to provide a facility to define classnames in the header of a user defined function. Rationale for this proposal is based on requirements for building Expert Systems with APL.
Structure of Expert Systems¶
An Expert System Shell is a collection of specialized software to support the many different types of activities that such a system requires. For example, a shell allows for the maintenance of “rules”, supports one or more “Inference Engines”, and provides for communications with external data sets. Of course it also supports many other functionalities.
To build an Expert System, the Knowledge Engineer starts with the shell, and adds specialized information, also known as the “Knowledge Base”. This information usually consists of a collection of rules to define the behaviour of the system, special hierarchical dictionaries to relate various technical terms of the domain, and may also include specialized software.
Over the last decade I have been studying the possibility of using APL (and APL2) to build such Expert Systems. As I gained experience, a number of concepts have evolved that integrate the unique aspects of APL with the paradigms that are commonly used in the field of Artificial Intelligence. It appears that no matter what the computer language, the specialised needs of a new paradigm always bump against the limitations imposed by that language. The solution of these problems has often in the past been to design new languages. I believe that a much better solution would be to expand the scope of existing language. This is especially true of APL.
The STSC APL*PLUS for the PC supports a number of features that facilitate the development of Expert Systems. There are, however, a number of features that would enhance even more the power of APL in building applications in the field of Artificial Intelligence. This note describes one such feature. It deals with a way to define classes of local names in the header of a function. Although I give a specific proposal, the form is not particularly important. What is important is the underlying concept. In what follows, I attempt to explain my justification for the need.
Shell and Domains¶
An Expert System deals with one or more domains of expertise. (These will hence be called just domains.) In its APL implementation, a domain may consist of a collection of APL functions, rules, variables, dictionaries, and other data structures. A cardinal rule of good Expert System design is a complete separation between the shell and the domain. This means that before the domain is invoked, the shell must not have ANY domain specific objects. When the domain dependent task is activated, the many domain objects are brought into the workspace, or are created. On completion, the results of the process are stored in appropriate objects, and the domain is wiped out from the workspace, returning to the clean initial state.
One might ask, why this need for separation? There are many reasons, but consider a complex problem that requires not one, but a series of domains to be applied sequentially. Then add the requirement for any one domain to call another domain as a kind of sub task. In such an environment any attempt to maintain global domain objects (functions and variables) would create chaos.
There is a simple way to separate domain objects from shell objects in APL. All one needs, is to define special domain specific functions called “drivers”. A driver function includes in its header the names of all domain specific functions and variables. Unfortunately, there is a limit on the length of defined function headers. This is a significant constraint in the design of some domains.
The need for restricting object names is not limited to domain drivers. An inference engine is a global object in the workspace, but it processes rule sets that are local to the domain. Such rule sets typically require communication of data between different rules. This can be handled by creation within rules of variables that must be global to all rules, but local within the function driving the rule set. Here again the natural APL solution is to define specific variables that are local to the APL inference engine driver. The more of these variables that can be defined in the header, the more flexibility one has in designing the rule set.
Individual rules also may be quite complex. In my approach, a rule contains at least three components. One is a (generally unique) tag that identifies the rule, another is the antecedent, and one is the consequent. Both the antecedent and the consequent are valid APL expressions. In the antecedent there may be any number of expressions, including assignments, separated by the APL*PLUS diamond. (Only the last expression must return a result. This result must be a boolean scalar.) The consequent may also contain multiple expressions separated by the diamond.
This structure and form of rules allows generation of variables that can be used in making decisions. These variables form a kind of scratch pad memory and should be local to the APL function that processes a single rule. This function, therefore, must also include local variable names that can be used only as long as that rule is being evaluated.
One way of thinking of rules is as a special type of defined function. A rule has a name, produces no result, and has no loops or branching other than a decision whether to evaluate the consequent. A rule also produces side effects that are global to the rule, but local to the rule set. Yet, a rule is not a separate object. It is a part of a larger object called a rule set. In my implementation, rule sets are defined as segmented strings (see APL*PLUS for definition of segmented strings). There is a segmented string for each component of a rule set. Rule sets are stored in component files when under development in the shell. For run time versions, rule sets can be brought into the workspace as segmented string variables.
I have named three types of APL defined functions that require localization of generic object names. These are the drivers for specific domains, inference engines to process rule sets, and individual rule processors. In some of these, the number of named objects may be so large that the current limit on the length of the header may be inadequate.
Implementation of Generic Variables¶
Although I have been severely constrained by the limit of header length, so far I have found ways of getting around such limitations. In general, I give single letter names to generic variables, and three letter names to domain specific APL functions. Within the inference engine function, eight single letter variables in lower case are global to all rules within a rule set. Within the rule evaluation function, another eight upper case letters are used for scratch pad memory. (These two functions also require many other local object names that are specific to the function task, so the header is quite long.) The remaining upper and lower case single letters are available for domain specific variables.
When a new domain-specific APL function is defined, the domain driver must also be re-defined by adding the new function name to its header. The access to the workspace of the newly defined function must also be defined. Some of this extra effort could be eliminated and the headers could be streamlined if APL supported the naming of generic object names.
In future I foresee domains where the number of named objects will exceed the available space in function headers. Hence the following proposal.
Proposal¶
One way to implement generic localization of names would be to apply the same techniques that are already in use elsewhere:
ONE*- would localize all names beginning with ONE
TWO?- would localize all four letter names TWOx
In addition I would suggest the following convention:
$aa- would localize all two letter names starting with two lower case letters
$Aa- would localize all two letter names starting with an upper case letter followed by a lower case letter
$a*- would localize all names of arbitrary length in lower case
$Aa*- would localize all names starting with an upper case letter followed by lower case letters.
I am not acquainted with the inner workings of APL interpreters. It is possible that some of my proposals may be impractical to implement. For example, the usage of the * cannot be implemented until specific named objects are created. This may be difficult to achieve, and even if possible may slow down the interpreter too much. The usage of the ? and of the $ on the other hand is more like a short hand notation, because the number of valid names defined by these symbols is limited.
If neither of my suggestions is feasible, there is still the possibility for expanding the maximum length of the header.