An expression using the list (and vector) brackets, and containing no
occurrences of the un-quoters "^
", "^^
" or "%" may LOOK as if it
represents a constant object, in the way that a word expression, e.g.
"cat" or a string expression does e.g. `the cat'. However, in Pop-11 an
expression like
[ a b { c d [ e { f } ] } [ g h ] ]compiles into a collection of instructions to CREATE a list of words, vectors and lists. Although the very same words are used each time (they are are stored in the Pop-11 dictionary for that purpose), the other structures are recreated each time the procedure is run. So for example if you define a procedure to return what looks like a constant list, it will return a new copy each time, thus:
define a_list(); [a list] ;;; create a list and leave on stack enddefine; ;;; Use it to create a list vars x1 = a_list(); x1 => ** [a list] ;;; create another list using the same procedure vars x2 = a_list(); x2 => ** [a list] ;;; test for equality x1 = x2 => ** <true> ;;; test for identity x1 == x2 => ** <false>So although the different lists returned by the procedure are = to each other (i.e. they are of the same type and they contain objects that are = to each other) nevertheless they are not == to each other. They are not the same object, even though they have the same structure and contain objects that are = to each other.
Similarly two apparently identical list expressions will create copies of each other but not identically the same object:
[a list] = [a list] => ** <true> [a list] == [a list] => ** <false>Contrast the case of words, which are identical if they look the same:
"list" == "list" => ** <true>Similarly, if a list is assigned to a variable outside a procedure then as long as nothing new is assigned to the variable, it will always point to the same list.
vars x2 = [another list]; x2 == x2 => ** <true>For more on the difference between "=" and "==" see HELP EQUAL
A consequence of all this is that a procedure that uses a list expression will create a new copy each time it runs, even though this may not be strictly necessary, and this can cause more garbage collections to occur than the program really needs. (The garbage collector is described briefly in Chapter 2). This will happen if you define a procedure that uses the pattern matcher and includes something like
if list matches [room ?name ?len ?width ?height] thenIn such cases the pattern to the right of "matches" will be recreated on every execution of the procedure.
For many programs where speed is not crucially important this will not matter as the Pop-11 garbage collector is very fast, and the creation of unnecessary temporary structures will not make very much difference. Where it does matter, there are two things you can do to ensure that exactly one list is created and then re-used.
If inside a list you declare a variable using lconstant, then you MUST initialise it, and the expression to initialise it will be evaluated only once, at compile time, and thereafter the same result will be used on every call of the procedure. E.g.
define the_list(); lconstant list = [the list]; list enddefine; the_list() => ** [the list] the_list() == the_list() => ** <true>If "lvars" or "vars" had been used instead of "lconstant" that last identity comparison would have been false.
Another way to get a truly constant constant list is to use these brackets which cause their contents to be evaluated once, at compile time and then the result is re-used each time the procedure is run.
define another_list(); #_< [another list] >_# enddefine; another_list() => ** [another list] another_list() == another_list() => ** <true>So you can use these brackets in expressions like
if list matches #_< .... >_# thenHowever this will NOT work properly if the list expression includes items whose values are not known until the procedure is actually running, ie.g. list or vector expressions containing any of
^ ^^ % ... %as described above.
If you use a truly constant list, and your program changes that list then the change will be remembered between invocations of the procedure, and the list will then not be a constant.
Form example, on each call of this procedure, it will return the value of item, which is got from the head of the stored list.
define counter() -> item; lconstant list = [0]; list(1) -> item; ;;; get the first item. item + 1 -> list(1); ;;; increment the stored value enddefine; counter() => ** 0 counter(), counter(), counter() => ** 1 2 3If "lconstant" were replaced by "lvars" then on every execution the procedure would construct a new list containing [0], and would therefore always print out the same result.
If a constant list is returned as a result, and then altered OUTSIDE the procedure that produced it, this will change the future behaviour of the procedure.
define announce() -> list; lconstant list = [0 is a number]; enddefine; announce() => ** [0 is a number]But if the result is tampered with....
vars x = announce(); 999 -> x(1); x => ** [999 is a number]then that "corrupts" the procedure:
announce() => ** [999 is a number]Some of the issues relating to constant lists are described in the online file HELP EFFICIENCY. The are two dangerous procedures described in REF sys_grbg_list and sys_grbg_destpair which explains how a list once created can be returned to the "free" store so that it can be re-used without creating unnecessary garbage collections. These procedures are dangerous and should only be used by very experienced programmers who know how to make sure when it a list really cannot be used again, and therefore can safely be returned to the free store.
It is precisely because that sort of thing can be very difficult to determine that Pop-11 uses an AUTOMATIC garbage collector, which is very much safer.