A common way to change an existing structure is to use a procedure that has an updater. An updater is a procedure that is invoked on the right side of an assignment arrow. A procedure and its updater together are sometimes referred to as a "doublet". For example, the Pop-11 procedure for accessing the first element of a list, called "hd" is really a doublet, consisting of two procedures: an accessor and an updater. To illustrate this, first create a list held in the variable person, and create three spare variables for use later.
vars person = [[name john][sex male][age 27]], p, x, y;We'll assign person to p, in order to illustrate some points later.
person -> p;Notice that p is not a COPY of person. It is the very same thing:
person == p => ** <true>The "accessor" of "hd" can be used in many different contexts, e.g.
hd(person) => ** [name john] hd(hd(person)) => ;;; get the first element of the first element ** name hd(person) -> y; ;;; assign the first element to y y => ** [name john] hd(hd(person)) -> x; x => ** nameNote that person is unchanged by this: the original first element is still there:
person => ** [[name john] [sex male] [age 27]]p is also unchanged:
p => ** [[name john] [sex male] [age 27]]As the previous examples show, the accessor of hd is invoked on the left hand side of an assignment arrow. We can invoke its updater on the right hand side. This will cause the list to be changed. Thus:
[name fred] -> hd(person); ;;; invoke updater of hd person => ** [[name fred] [sex male] [age 27]]The value of p is still the same as the value of person, so p is also changed:
p => ** [[name fred] [sex male] [age 27]]But y has been left unchanged:
y => ** [name john]We can use y to restore the original hd(person) thus:
y -> hd(person); person => ** [[name john] [sex male] [age 27]] p => ** [[name john] [sex male] [age 27]]The next example is more subtle. We are going to change the first element of the first element of person. I.e. hd(hd(person)). This means that there will be two occurrences of "hd" on the right of the assignment arrow. But one of them will not be invoked as an updater, i.e. the one inside the brackets: that is used access the item that will be updated by the call of hd outside all the brackets thus:
"forename" -> hd(hd(person));Check the result:
person => ** [[forename john] [sex male] [age 27]]As before this affects p also, because it is still "pointing to" the very same thing as person. What about y?
y => ** [forename john]Because y was the very same thing as the first item of person, changing part of the first item of person also changed part of y, unlike the case where replacing the whole of the first item of person left y unchanged.
One way to think of this is to think of a variable as a label attached to its value by a piece of string. When you assign something to a variable, you keep the same label, but you attach the string to something else. So
hd(person) -> y;Takes the object that was the first element of person, and connects it to the end of y's string. Then an instruction like:
"forename" -> hd(hd(person));changes the contents of the first item of the list person, and so that changes the contents of the item to which y's string is attached.
By contrast the instruction
[name fred] -> hd(person);replaces the thing that was the first element of person, but y's string is still connected to what it was previously connected to, which is no longer part of the list person. So this does not change y at all. And if we THEN do
"forename" -> hd(hd(person));it will not affect y.
This example should help to show why programming with side-effects can be confusing, and mistakes can be made. However, it is also often very convenient, as long as you thing VERY clearly about what you are doing!