{Begin SubSec Loops and the Interlisp System} {Title Loops and the Interlisp System} {Text {Begin SubSec Saving Class and Instance Definitions on Files} {Title Saving Class and Instance Definitions on Files} {Text Loops has been integrated with the Interlisp file system to allow saving of class definitions on files. The file command: {lispcode (CLASSES * {arg classNameList})}{index CLASSES FileCom} added to the filecoms of any file will allow one to dump out the prettyprinted version of the source you see when you edit the class definition. These class names can be listed in any order in a single list, provided that all super classes of a class on the list are on the list as well, or will be previously defined. {lispcode (INSTANCES * {arg instanceNameList})}{index INSTANCES FileCom} added to the filecoms of any file will allow one to dump out the prettyprinted versions of named instances, as well as any unnamed instances that they point to. Functions used to implement methods are ordinary Interlisp functions. Those that are named automatically by Loops as {lisp {arg className}.{arg selector}} start with the same characters; they will be found alphabetically together on any function list which is created. The function {fn CalledFns} ({PageRef Fn CalledFns}) can be used get a list of all functions used by a list of classes. }{End SubSec Saving Class and Instance Definitions on Files} {Begin SubSec Classes for Lisp Datatypes} {Title Classes for Lisp Datatypes} {Text {note /* warning: this facility has not been adequately debugged. */} One can use the message sending protocol with records (lists) whose first element is a class, or ordinary Interlisp datatypes. In the first case, the first element is used as the class to look up the method to be used. In the second case, the class is found using the function {lisp (GetLispClass {arg obj})},{index GetLispClass Fn} which looks it up in the hash table {var LispClassTable},{index LispClassTable Var} based on the type name of the datatype. We call datatypes with associated classes and records with first element a class {it pseudoclasses},{index pseudoclasses} and instances of them {it pseudoinstances}.{index pseudoinstances} If {fn GetValue} or {fn PutValue} are called with {arg self} bound to a pseudoinstance, then the method associated with the selector {lisp GetValue} in the pseudoclass (call it {lisp PC}) is called as follows: {lispcode (APPLY* (GetMethod PC 'GetValue) {arg instance} {arg varName} {arg propName})} or {lispcode (APPLY* (GetMethod PC 'PutValue) {arg instance} {arg varName} {arg newValue} {arg propName})} If the associated class {lisp PC} has a {lisp GetValue} ({lisp PutValue}) method, then values of the variables can be found. This allows a mixture of compiled access to datatype fields, and interpreted access within Loops. }{End SubSec Classes for Lisp Datatypes} {Begin SubSec Some Details of the Loops implementation} {Title Some Details of the Loops implementation} {Text Methods are implemented by Lisp functions. The message sending expression: {lispcode (← {arg object} {arg selector} {arg arg{sub 1}} {ellipsis} {arg arg{sub N}})} is expanded as a compiler MACRO into {lispcode (APPLY* (FetchMethodOrHelp {arg object} '{arg selector}) {arg object} {arg arg{sub 1}} {ellipsis} {arg arg{sub N}})} {index FetchMethodOrHelp Fn} {note FetchMethodOrHelp or GetMethod ???} {fn GetMethod} returns the name of the Interlisp function associated with {arg selector} anywhere in the class of {arg object}, or in the superClass chain of that class. Notice that the object is implicitly included as the first argument of the function, as well as being the argument for {fn GetMethod}. By syntactic convention the first argument (bound to the object) in any function which is being used as a method is called {lisp self}. The expression for the object is evaluated only once. Objects in Loops are represented in memory as Interlisp datatypes. The datatypes for classes have property lists for methods, class variables, instance variables, and their properties. Datatypes for instances have property lists for instance variables and their properties. In general, the selector names and variable names are stored in the class objects. When instances are read in from a data base, they have their local name tables aligned with the class standards. Special provisions are provided for handling instances whose variable names do not correspond to current class definitions. Instances act as if they have local tables for lookup of variables and properties, but they usually share the class name table and no storage is actually allocated for local tables unless it is needed. Default values for instance variables and properties are not copied to an instance. No space for instance variables or properties is allocated until that variable or property has been set individually for the instance. This means that the default values are not just initial values. In particular, if a class is altered to change the default value of an instance variable, then all of the instances that do not have individualized values will reflect the new default value. Also, there is no storage overhead in instances for unchanged properties (e.g., for documentation) defined in classes. Since individualized values of variables are stored in the instances, there is no need to search the class hierarachy after a variable or property has been set in the instance. In contrast, since class variables are shared among instances it is always necessary to go to the class (or a super class) to get a value. Although many of the ideas of the Loops database were inspired by PIE, the implementation differs along several dimensions. PIE was intended primarily for use with a browser (i.e., an interactive viewing and editing program), and efficiency was not a primary concern. Since Loops was intended for use by programs with potentially extensive computational processes, a need for efficient access was perceived and this led to some different tradeoffs in the choice of implementation. One difference between PIE and Loops is the grainsize of the changes written in layers. PIE performs separate bookkeeping on changes to values of every variable in objects. Loops avoids the storage penalty of this by keeping track only of which objects have been changed. This means that file layers in PIE contain partial objects (e.g., a change to a single variable) while layers in Loops contain complete objects. In effect, Loops economizes on space (and time) in memory instead of space in the databases. Another difference is that the Loops implementation tries to reduce the cost of references to values by snapping links to references. However, link snapping is fundamentally in conflict with a lookup process that takes an environment as an argument. Link snapping precludes the sharing of objects between environments in those cases where the interpretation of the references in the shared objects is sensitive to the environment. Loops preserves a complete isolation of environments, with exchange of information permitted only as a knowledge base transaction. In general, realigning an environment to incorporate changes from another environment requires writing out the changes, clearing the memory in the environments, and re-opening the associated knowledge bases. In contrast, PIE always shared information between contexts, but it paid the overhead of reinterpreting the symbolic addresses repeatedly at every reference. {note $foo and #$FOO etc. as read macros.} }{End SubSec Some Details of the Loops implementation} }{End SubSec Loops and the Interlisp System}