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 =>
** name
Note 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!