My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
ProgramStructure  
Program Structure in Teyjus
Updated Jun 10, 2010 by gopalan....@gmail.com

The Structure of Teyjus Programs

Programming in Lambda Prolog is structured around the definition of modules. In Teyjus, there are four files associated with a module. If the name of the module is <modname> then these files are <modname>.mod, <modname>.sig, <modname>.lpo and <modname>.lp, and they correspond, respectively, to the source code, the signature, the bytecode file generated by the compiler for the module and the linked and executable version of the bytecode file produced by the linker. The user of Teyjus is not concerned with the internal structure of either version of the bytecode file and it suffices for him/her to know merely of their existence. It is necessary, however, to understand the structure and purpose of the signature and source code file in order to produce meaningful Lambda Prolog programs that can be compiled, linked and executed under Teyjus. The details of these two kinds of objects are presented in the part of this manual that describes Teyjus syntax. Before looking at the syntactic aspects, however, it is useful to understand the intended roles of these two kinds of objects in programming.

A signature is associated with a module to determine an external view for it. In particular, a signature identifies names of sorts, type constructors, constants and operators and also might indicate that certain predicates are to be used in the module without changes in their definitions or that a fixed definition for these predicates is to be exported from that module. Signatures are eventually employed in two ways: (a) when a module is being compiled, the signature is used to determine whether the module is "living up to its promise" and also to qualify some of the declarations in the module and (b) when some other module imports or accumulates this one, the signature determines what is externally visible and hence plays a role in certifying the module interactions.

Two technical terms are used in the above paragraph to describe possibilities for module interactions: importing and accumulating. We pause briefly to explain these kinds of interactions.

  • Importation is a process by which the globally visible procedures defined in a particular module become dynamically available in solving the bodies of procedures in another module. Note that this is a primitive that actually causes a change in program context at runtime.
  • Accumulation is a primitive that calls for the accretion of the code in named and independently defined modules into a new, larger, module. This is a primitive that has a largely static impact: a simple-minded implementation---different from the one used in Teyjus---could realize accumulation through a compile time inlining of code in the accumulated modules.

While accumulation evokes the image of (static) inlining of code, it is important to note that the inlining is not entirely naive. In Lambda Prolog it is possible to scope constant names and procedure definitions over modules. For names, this is achieved via the notion of a local constant: any constant that is not defined in the signature for the module is interpreted as being local. For procedures, this is manifest in the scopes of imports (and also in explicit implication goals). The inlining corresponding to accumulation is to be done in a way that respects scopes. In particular, locals in an accumulated module are to be treated as being distinct even if there is a coincidence in names with the constants in another accumulated module or in the accumulating module. Similarly, imports in an accumulated module have only the code originating in that module as their scope.

The semantics of module importation, as we have noted above, requires the program context to change in the course of execution. Realizing such context switches can be computationally costly, and this is made even more so by the backtracking behaviour inherent in logic programs. For a while it was thought that importation was needed to realize scoping over procedure definitions. However, this turns out not to be true: with the ability to localize names, the hiding of predicate definitions can be realized even by using the simpler device of module accumulation. In fact, using this latter approach often results in code with better modularity features. Thus, while the present version of Teyjus continues to support module importation for the sake of backward compatibility, the use of this feature in any new code that is written is strongly discouraged.

We have described the meaning of module accumulation as one corresponding to a careful inlining of code. However an actual implementation does not need to do such inlining at compile time. The present Teyjus system, in fact, relegates this inlining to linking time and thereby supports the separate compilation of each module. Separate compilation of this sort with respect to module importation should be even more easy to visualize and is also supported. Of course, before the code produced by the compiler from any module can be run, it must be combined with the code obtained from the modules it accumulates or imports. This is exactly the role played by the linker and it also highlights the difference between the .lpo and the .lp files.

We have explained the forms of module interaction and also the intended role of signatures in providing interfaces for such interactions only informally here. The reader interested in actually using Teyjus should proceed to the page discussing the syntax of Teyjus programs in order to understand the forms of module interaction and the notion of signature matching more precisely.


Sign in to add a comment
Powered by Google Project Hosting