next up previous contents
Next: Procedure definitions are Up: CHAPTER.2: INTRODUCTION TO Previous: Pop-11 Expressions: some

Imperatives: some examples

Printing

We have already met the use of "=>" in an imperative to print something out. For example the following prints out a list of words:

    [tom dick harry] =>
    ** [tom dick harry]

Assignment

Another very common form of imperative is an `assignment', using the assignment arrow `->'. Here is how you assign a number to be the value of the variable `x' and another to the value of y:

    99 -> x;
    99 + 5 -> y;
In both cases the left hand side is an expression which denotes a number. The whole thing says: `take the thing denoted by the expression on the left, and make the thing on the right (the variable x or y), refer to it from now on. Thus, in the second example y will refer to 104. This can be tested with another imperative:

   y =>
    ** 104
Normally an imperative which does not end with the print arrow "=>", must end with a semi-colon, which is an "imperative separator". Later we'll see that if an imperative is embedded in a larger structure it does not always need the semi-colon to terminate an imperative or expression.

A few more examples of assignments:

To give the variable x the value 33 do:

    33 -> x;
to give the variable y twice the value of x do:

    x + x -> y;
OR

    (x + x) -> y;
Notice the difference between using the variable on the left and on the right of an assignment. On the left its existing value is USED. On the right a new value is SET, or assigned. So to assign the value of x to y do

    x -> y;
To assign a list of numbers to list1 do something like:

    [ 1 2 3 4 5 ]  -> list1;
To assign a list of words to list2:

    [cat dog mouse elephant] -> list2;
To assign a list like list1 but in reverse order to x, do:

    rev(list1)  -> x;
    x  =>
    ** [5 4 3 2 1]
Although what rev does is intuitively clear, stating precisely what it does is slightly tricky: rev takes as input (off the Pop-11 stack) a list L1 and creates a new list L2 containing the same elements as L1, but in the opposite order. L2 is returned as the output of rev.

Notice that rev does NOT reverse the orignal list: that can remain totally unchanged, as can be tested:

    list1 =>

Multiple assignment

Pop-11 allows you to assign to several variables at the same time, using a single assignment arrow. First we declare three variables, then we assign the numbers 111, 222, and 333 to them.

    vars num1, num2, num3;
    111, 222, 333 -> (num1, num2, num3);

    num3 =>
    ** 333
(Multiple assignments did not work in the earliest versions of Pop-11).

Declarations of dynamic and lexical (static) variables.

Declarations may be used to introduce new variable names. E.g.

    vars x, y;
This `declares' to Pop-11 that you wish to use the names `x' and `y' as names for objects. It is a global and dynamic variable declaration. Any number of variables may be introduced in one declaration. The same syntax is used for introducing both global variables and variables that are local to a procedure, explained below.

Lexical variables are declared using a similar syntax, but using "lvars" instead of "vars".

    lvars num, lista, listb;
declares three lexical variables.

The difference between lexical and non-lexical (= dynamic) variables is rather subtle and will not be explained fully here (but see the Note below). The main difference is that a variable declared using "vars" is dynamic and global and can be potentially accessed by any procedure, no matter whether it is defined in the same file as the "vars" declaration or somewhere else, whereas a lexical variable has a more restricted "scope".

Such a variable can usually be accessed only by instructions in the procedure in which the variable was declared, or by procedures in the same file, or by procedures in the same compilation stream, as explained in the file HELP LEXICAL. The word "lexical" is used to indicate that the scope of the variable is determined by the textual context in which it is declared.

For most purposes programs will run more quickly and be less liable to errors if the variables which are local to a procedure are declared as lexical using "lvars". (However, if variables are to be used in connection with the Pop-11 pattern matcher, explained in a later chapter, they may need to be declared using "vars", not "lvars".)

In some Poplog implementations some of the lvars will allocated to registers. E.g. on the VAX, the first two lvars in each procedure are treated as names of registers, for efficiency. On SPARC machines, the first seven lvars are held in registers. (These numbers may change.)

There are, in fact many different sorts of declarations, used for telling the Pop-11 compiler that a symbol is to have special properties, but they will not be discussed in detail in this primer.

For more information see these online Poplog files:

    REF SYNTAX, REF POPSYNTAX, HELP VARS, HELP LEXICAL
For many purposes it does not matter whether you use "vars" or "lvars". However using "vars" rather than "lvars" for local variables can sometimes lead to obscure bugs. Also "lvars" variables are generally more efficient and allow some more powerful constructs to be used when procedures create new procedures while running. (Advanced programmers can get more information from the HELP LEXICAL and HELP LVARS files. The latter explains how so-called `lexical closures' can be produced in Pop-11, as happens in functional languages such as Scheme, ML, Miranda and Haskell.)

Normally, unless you have a special reason for using "vars", such as wanting to use the Pop-11 pattern matcher (explained in TEACH MATCHES, and in Chapter 7, below), you should always use "lvars" for local variables.

For making changes to a Pop-11 global variable constrained to a particular procedure, the local declaration "dlocal" may be used, as explained in HELP DLOCAL. This is normally required only for advanced programming.

There is a slightly less convenient version of the matcher, described in HELP FMATCHES which works with lvars, and can be used by more experienced programmers, e.g. together with "sections". A more up to date version of this known as DOESMATCH, with associated libraries, is available from the Birmingham Poplog FTP site, along with documentation.

Declarations may contain initialisations.

It is often convenient to combine a declaration followed by an assignment into a single imperative. Thus, instead of writing the following:

    vars list1, list2;
    lvars num1, num2;

    [a b c] -> list1;
    list1 <> [d e f] -> list2;

    33 -> num1;
    num1 * 2 -> num2;
You can write the more compact, and perhaps clearer version:

    vars
        list1 = [a b c],
        list2 = list1 <> [d e f];

    lvars num1 = 33, num2 = num1 * 2;
Note that each initialisation consisting of the form

    <variable> = <expression>
MUST be followed by a comma, unless it is the final one, in which case the semicolon ends the list of declarations.

WARNING: people who are used to using the symbol "=" as an assignment symbol may be confused into thinking that it can be used for assignments from right to left in Pop-11, like "=" in C, and ":=" in Pascal. This is not so. In Pop-11 "=" is used for initialising a variable ONLY in the scope of a variable declaration, such as "vars" or "lvars" or "dlocal", etc. In other contents the assignment arrow "->" should be used, as shown above.

Programmers who forget this point and write things like

    z = x + y;
will in fact not assign a value to z but create an expression which does a comparison of the two values on the left and right of "=", which will result in the value TRUE or the value FALSE being produced. That value is left on the Pop-11 stack, as explained above.

Variables and constants

If you wish an identifier to be given a value once only, which is never changed thereafter, you can declare it using "constant" or "lconstant", the former being used for global constants, to be accessible everywhere, and latter being used for lexically scoped identifiers, usually accessible in only one file or one procedure. Constant declarations may also include initialisations.

Examples are

    constant number_of_rooms = 66;

    lconstant pattern = [ ?subject ?verb ?object];

Using mixed case and underscores in variables

Note: Pop-11 is a mixed-case language, so that each of the following:

    list, LIST, List, lisT
is treated as different, and they can be used as names of different variables (though this is likely to cause confusion). Most system words use only lower case, though many of the identifiers concerned with the X window system include upper case letters.

Users may declare variables using upper or lower case. So:

    lvars cat, Cat;
declares two different variables.

Occasionally we use upper case in our text, to refer to lower case identifiers, e.g. IF and ENDIF in order to make the words stand out.

One issue on which opinions vary is whether to use capital letters or "underscores" to form long identifier names, e.g. some people would call a procedure

    transform_input_list
whereas others would prefer

    TransformInputList
There is no right answer. Many Pop-11 and VED system identifiers use underscores, e.g. procedures used by the compiler, such as:

    pop11_comp_constructor   pop11_comp_declaration
    pop11_comp_expr_seq      pop11_comp_expr_seq_to
    pop11_define_declare     pop11_define_props
    pop11_exec_stmnt_seq_to  pop11_loop_end
    pop11_loop_start         pop11_need_nextitem
On the other hand, many of the system identifiers concerned with the interface to the X window system use mixed case, following the conventions of the X system, e.g.

    XpolAppContext      XptActionHookWrapper    XptActionList
    XptCheckCacheType   XptCheckCallbackList    XptCheckKeySymTable
    XtWindowOfObject    XtUnmapWidget           XtUnrealizeWidget
    XpwSetFont          XpwTextWidth            XptCallbackList
and hundreds more!

Using undeclared variables

Variables may be used without being declared. This is equivalent to declaring them globally, except that Pop-11 prints out a warning message.

    3 -> xx;
    ;;; DECLARING VARIABLE xx       (Printed by Pop-11)
If you attempt to declare a variable which is already part of the Pop-11 system vocabulary, for example:

    vars define;
you will get a mishap message:

    ;;; MISHAP - IDW: ILLEGAL DECLARATION OF A WORD

NOTE on variables for experienced computer scientists:

Like many implementations of the language LISP, Pop-11 has two kinds of identifiers:

(a) Lexically scoped identifiers (declared using "lvars", "lconstant", and "dlvars", as explained in HELP LEXICAL, HELP LVARS), which can only be accessed within the smallest enclosing text block in which they are declared. Such a text block may be any of the following:

    o   a procedure
    o   a lexical block delimited by "lblock  ... endlblock"
    o   a file
    o   a "compilation stream" (See REF PROGLIST)
(b) Dynamically scoped or "permanent" identifiers (usually either declared globally using "vars" or "constant", or made local to a procedure using "vars" or "dlocal"). Once these have been declared, they can be accessed globally, that is by expressions using those variables in other procedures, other files, etc. (The section mechanism described later (and in HELP SECTIONS) can be used to restrict unwanted access to permanent, dynamically scoped, identifiers.)

Dynamically scoped variables can in principle be implemented using either "deep binding" or "shallow binding".

o Deep binding, as used in Interlisp, stores variables and their values in a list, often called "oblist". This has the form of a list of name-value pairs, so that access to the value of a variable requires searching along the list to find the "most recent" binding for the variable. Starting a new procedure P in which X is a local variable merely requires adding a pair of items to the list, X and its value. While P is active, accessing or updating the value of X will use that most recent occurrence, ignoring others further along the list, representing local variables of other currently active procedures higher up the calling chain. However if another procedure Q is invoked by P or by something it invokes, and Q uses X as local, then Q will add another name-value pair for X to the front of the list, thereby "shadowing" the pair added by P. When Q procedure finishes it removes all the name-value pairs corresponding to its own local variables from the list, so that the previous ones will again be found by searching along it. Similarly when P finishes it will remove its name-value pairs allowing older bindings for X to be accessed.

o Shallow binding, by contrast, always stores the value of a variable, e.g. X, in the same portion of memory. This means that when a procedure P in which X is a dynamic local variable starts up, the existing value of X has to be saved somewhere (sometimes referred to as the "auxilliary stack"), so that the value can be restored when P finishes execution. The saved values need to be stacked, since whenever a procedure using X as local finishes, the top of the stack for X is copied back to the standard location for values of X and the stack shortened by one.

Shallow binding makes accessing values much faster than deep binding since no searching along a list is required. However deep binding makes context switching easier since all that's needed is to add or remove an initial segment of oblist, requiring a few pointers to be manipulated, whereas shallow binding requires all the values of local variables to saved (by copying) when a context is entered, and copied back when the context is left. Pop-11, like some, but not all, Lisp systems, uses shallow binding for dynamic local variables.

Lexical variables are more efficient than both, and introduce fewer bugs due to unintended interactions between programs, but they can be less flexible in some ways. Older versions of Pop-11, like older versions of Lisp, had ONLY dynamic variables. So the older text books for Pop-11 and older TEACH files use only "vars", even in situations where "lvars" would be preferable. This Primer uses "lvars" by default, except where "vars" is needed, e.g. for pattern matcher variables, or global variables.

There are some people who dislike lexical scoping and use only "vars". There are others who think that only lexical scoping should be used, and who never use "vars" for local variables!

(End NOTE on variables for experienced computer scientists.)



next up previous contents
Next: Procedure definitions are Up: CHAPTER.2: INTRODUCTION TO Previous: Pop-11 Expressions: some



Aaron Sloman
Fri Jan 2 03:17:44 GMT 1998