summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2015-05-25 22:14:18 -0700
committerKaz Kylheku <kaz@kylheku.com>2015-05-25 22:14:18 -0700
commitb0906871e8a6bbd2f5b4c25eca511ac1827a30d5 (patch)
tree05a4896092a797e0c3d5bb0ca6244fe1695fec44
parent535999c69576b3e57e35f1936bac852c6aedc983 (diff)
downloadtxr-b0906871e8a6bbd2f5b4c25eca511ac1827a30d5.tar.gz
txr-b0906871e8a6bbd2f5b4c25eca511ac1827a30d5.tar.bz2
txr-b0906871e8a6bbd2f5b4c25eca511ac1827a30d5.zip
* txr.1: Document new syntactic places framework and place-mutating macros.
-rw-r--r--ChangeLog4
-rw-r--r--txr.12601
2 files changed, 2320 insertions, 285 deletions
diff --git a/ChangeLog b/ChangeLog
index e5d64fb4..8c0eaacb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2015-05-25 Kaz Kylheku <kaz@kylheku.com>
+ * txr.1: Document new syntactic places framework and place-mutating macros.
+
+2015-05-25 Kaz Kylheku <kaz@kylheku.com>
+
Fix mismanaged dyn_env variable.
* eval.c (bindings_helper): In the parallel binding case, only allocate
diff --git a/txr.1 b/txr.1
index a2818d83..c9dc6127 100644
--- a/txr.1
+++ b/txr.1
@@ -9750,6 +9750,353 @@ form doesn't specify a value, and the variable doesn't exist, it is
created with a value of
.codn nil .
+.SS* Syntactic Places and Accessors
+
+The \*(TL feature known as
+.meta syntactic places
+allows programs to use
+the syntax of a form which is used to
+.I access
+a value from an environment or
+object, as an expression which denotes a
+.I place
+where a value may be
+.I stored.
+
+They are almost exactly the
+same concept as "generalized references" in Common Lisp, and are related to
+"lvalues" in languages in the C family, or "designators" in Pascal.
+
+.NP* Symbolic Places
+
+A symbol is a is a syntactic place if it names a variable. If
+.code a
+is a variable, then it may be assigned using the
+.code set
+operator: the form
+.code (set a 42)
+causes
+.code a
+to have the integer value 42.
+
+.NP* Compound Places
+
+A compound expression can be a syntactic place, if its leftmost constituent is
+as symbol which is specially registered, and if the form has the correct syntax
+for hat kind of place, and suitable semantics. Such an expression is a compound
+place.
+
+An example of a compound place is a
+.code car
+form. If
+.code c
+is an expression denoting a
+.code cons
+cell, then
+.code (car c)
+is not only an expression which retrieves the value of the
+.code car
+field of the cell. It is also a syntactic place which denotes that field as
+a storage location. Consequently, the expression
+.cblk
+(set (car c) "abc")
+.cble
+stores the character string
+.str "abc"
+in that location. Although the same effect can be obtained with
+.cblk
+(rplaca c "abc")
+.cble
+the syntactic place frees the programmer from having to remember
+different update functions for different kinds of places.
+There are
+various other advantages. \*(TL provides a plethora of operators
+for modifying a place in addition to
+.codn set .
+Subject to certain usage restrictions, these operators work uniformly on all
+places. For instance, the expression
+.code (rotate (car x) [str 3] y)
+causes three different kinds of places to exchange contents,
+while the expressions
+.codn x ,
+.codn str ,
+and
+.code y
+are accessed only once. New kinds of place update macros like
+.code rotate
+are quite easily defined, as are new kinds of compound places.
+
+.NP* Accessor Functions
+
+When a function call form such as the above
+.code (car x)
+is a syntactic place, then the function is called an
+.IR accessor .
+This term is used throughout this document to denote functions
+which have associated syntactic places.
+
+.NP* Macro Call Syntactic Places
+
+Syntactic places can be macros (global and lexical), including symbol macros.
+So for instance in
+.code (set x 42)
+the
+.code x
+place can actually be a symbolic macro which expands to, say,
+.codn (cdr y) .
+And so then, the assignment is effectively
+.codn (set (cdr y) 42) .
+
+.NP* User-Defined Syntactic Places and Place Operators
+
+Syntactic places, as well as operators upon syntactic places,
+are both open-ended. Code can be written quite easily in \*(TL to introduce
+new kinds of places, as well as new place-mutating operators.
+New places can be introduced with the help of the
+.code defplace
+macro. New place update macros (place operators) are written using the
+ordinary macro definer
+.code defmacro
+but with the help of special utility macros called
+.codn with-update-expander ,
+.codn with-clobber-expander ,
+and
+.codn with-delete-expander .
+Simple update macros similar to
+.code inc
+and
+.code push
+can be written compactly using
+.codn define-modify-macro .
+
+.NP* Deletable Places
+
+Unlike generalized references in Common Lisp, \*(TL syntactic
+places support the concept of deletion. Some kinds of places (not all)
+can be deleted, which is an action distinct from (but does not preclude) being
+overwritten with a value. What exactly it means for a place to be deleted,
+or whether that is even permitted, depends on the kind of place.
+For instance a place which denotes a variable may not be deleted.
+A place which denotes a hash table entry may be deleted, and results in the
+entry being removed from the hash table. Deleting a place in a list
+causes the trailing items, if any, or else the terminating atom, to
+move in to close the gap. Users may, of course, define new kinds of places
+which support deletion semantics.
+
+.NP* Evaluation of Places
+
+To bring about their effect, place operators must evaluate one or
+more places. Moreover, some of them evaluate additional forms which are not
+places. Which the arguments of a place operator form are places and which are
+ordinary forms depends on its specific syntax. For all the built-in place
+operators, the position of an argument in the syntax determines whether it is
+treated as (and consequently required to be) a syntactic place, or whether it is
+an ordinary form.
+
+All built-in place operators perform the evaluation of place and non-place
+argument forms in strict left to right order.
+
+Place forms are evaluated not in order to compute a value, but in order to
+determine the storage location. In addition to determining a storage location,
+the evaluation of a place form may possibly give rise to side effects.
+Once a place is fully evaluated, the storage location can then be accessed.
+Access to the storage location is not considered part of the evaluation of a
+place. To determine a storage location means to compute some hidden referential
+object which provides subsequent access to that location without the need for a
+re-evaluation of the original place form. (The subsequent access to the
+place through this referential object may still require a multi-step traversal
+of a data structure; minimizing such steps is a matter of optimization.)
+
+Place forms may themselves be compounds, which contain subexpressions that must
+be evaluated. All such evaluation for the built-in places takes place in left
+to right order.
+
+Certain place operators, such as
+.code shift
+and
+.codn rotate ,
+exhibit an unspecified behavior with regard to the timing of the access
+of the prior value of a place, relative to the evaluation of places
+which occur later in the same place operator form. Access to the prior values
+may be delayed until the entire form is evaluated, or it may be interleaved
+into the evaluation of the form. For example, in the form
+.codn (shift a b c 1) ,
+the prior value of
+.code a
+can be accessed and saved as soon as
+.code a
+is evaluated, prior to the evaluation of
+.codn b .
+Alternatively,
+.code a
+may be accessed and saved later, after the evaluation of
+.code b
+or after the evaluation of all the forms. This issue affects the behavior of
+place-modifying forms whose subforms contain side effects. It is recommended
+that such forms not be included in programs.
+
+.NP* Nested Places
+
+Certain place forms are required to have one or more arguments which
+are themselves places. The prime example of this, and the only example from
+among built-in syntactic places, are DWIM forms. A DWIM form has the syntax
+
+.cblk
+.mets (dwim < obj-place < index <> [ alt ])
+.cble
+
+and of course the square-bracket-notation equivalent:
+
+.cblk
+.mets >> [ obj-place < index <> [ alt ]]
+.cble
+
+Note that not only is the entire form a place, denoting some element or element
+range of
+.metn obj-place ,
+but there is the added constraint that
+.meta obj-place
+must also itself be a syntactic place.
+
+This requirement is necessary, because it supports the behavior that
+when the element or element range is updated, then
+.meta obj-place
+is also potentially updated.
+
+After the assignment
+.cblk
+(set [obj 0..3] '("forty" "two"))
+.cble
+not only is the range of places denoted by
+.code [obj 0..3]
+replaced by the list of strings
+.cblk
+("forty" "two")
+.cble
+but
+.code obj
+may also be overwritten with a new value.
+
+This behavior is necessary because the DWIM brackets notation maintains
+the illusion of an encapsulated array-like container over several dis-similar
+types, including Lisp lists. But Lisp lists do not behave as fully
+encapsulated containers. Some mutations on Lisp lists return new objects,
+which then have to stored (or otherwise accepted) in place of the original
+objects in order to maintain the array-like illusion.
+
+Of course, this special double processing extends to an
+arbitrary nesting depth. For instance, consider an assignment
+such as
+.codn (set [[obj 'a] 0] 42) .
+Here, three places are affected by the operation: namely,
+.codn obj ,
+.cblk
+[obj 'a]
+.cble
+and
+.codn [[obj 'a] 0] .
+
+.NP* Built-In Syntactic Places
+
+The following is a summary of the built-in place forms, in addition to symbolic
+places denoting variables. Of course, new syntactic place forms can be
+defined by \*(TX programs.
+
+.cblk
+.mets (car << object )
+.mets (first << object )
+.mets (cdr << object )
+.mets (rest << object )
+.mets (vecref < vec << idx )
+.mets (chr-str < str << idx )
+.mets (gethash < hash < key <> [ alt ])
+.mets (dwim < obj-place < index <> [ alt ])
+.mets >> [ obj-place < index <> [ alt ]] ;; equivalent to dwim
+.mets (symbol-value << symbol )
+.mets (symbol-function << symbol )
+.mets (fun << function-name )
+.mets (force << promise )
+.mets (errno)
+.cble
+
+.NP* Built-In Place-Mutating Operators
+
+The following is a summery of the built-in place mutating macros.
+They are described in detail in their own sections.
+
+.meIP (set >> { place << new-value }*)
+Assigns the values of expressions to places, performing assignments in left to right order,
+returning the value assigned to the rightmost place.
+
+.meIP (pset >> { place << new-value }*)
+Assigns the values of expressions to places, performing the determination of
+places and evaluation of the expressions left to right, but the assignment
+in parallel. Returns the value assigned to the rightmost place.
+
+.meIP (zap < place <> [ new-value ])
+Assigns
+.meta new-value
+to place, defaulting to
+.codn nil ,
+and returns the prior value.
+
+.meIP (flip << place )
+Logically toggles the boolean value of
+.metn place ,
+and returns the new value.
+
+.meIP (inc < place <> [ delta ])
+Increments
+.meta place
+by
+.metn delta ,
+which defaults to 1, and returns the new value.
+
+.meIP (dec < place <> [ delta ])
+Decrements
+.meta place
+by
+.metn delta ,
+which defaults to 1, and returns the new value.
+
+.meIP (swap < left-place << right-place )
+Exchanges the values of
+.meta left-place
+and
+.metn right-place .
+
+.meIP (push < item << place )
+Pushes
+.meta item
+into the list stored in
+.code place
+and returns
+.codn item .
+
+.meIP (pop << place )
+Pop the list stored in
+.meta place
+and returns the popped value.
+
+.meIP (shift << place + << shift-in-value)
+Treats one or more places as a "multi-place shift register".
+Values are shifted to the left among the places. The
+rightmost place receives
+.metn shift-in-value ,
+and the value of the leftmost place emerges as the return value.
+
+.meIP (rotate << place *)
+Treats zero or more places as a "multi-place rotate register".
+The places exchange values among themselves, by a rotation
+by one place to the left. The value of the leftmost place
+goes to the rightmost place, and that value is returned.
+
+.meIP (del << place )
+Deletes a place which supports deletion, and returns
+the value which existed in that place prior to deletion.
+
+.PP
+
.SH* TXR LISP OPERATOR AND FUNCTION LIBRARY
A compound expression with a symbol as its first element, if
intended to be evaluated, denotes either an operator invocation or a function
@@ -10609,6 +10956,8 @@ and so the second pprint form is not evaluated.
.synb
.mets (dwim << argument *)
.mets <> [ argument *]
+.mets (set (dwim < obj-place < index <> [ alt ]) << new-value )
+.mets (set >> [ obj-place < index <> [ alt ]] << new-value )
.syne
.desc
The
@@ -10705,7 +11054,7 @@ object to which the first argument expression evaluates. The possibilities
are:
.RS
.meIP >> [ function << argument *]
-Call the given function object to the given arguments.
+Call the given function object with the given arguments.
.meIP >> [ symbol << argument *]
If the first expression evaluates to a symbol, that symbol
@@ -10716,21 +11065,18 @@ given arguments.
.meIP >> [ sequence << index ]
Retrieve an element from
.metn sequence ,
-given by the integer
-.metn index .
-
-The following equivalences hold:
-
-.cblk
- [seq index] <--> (ref seq index)
+at the specified
+.metn index ,
+which is a nonnegative integer.
- (set [seq index] new) <--> (refset seq index new)
-.cble
+This form is also a place if the
+.meta sequence
+subform is a place. If a value is stored to this place, it replaces the
+element.
-This form accepts update operators like
-.code inc
-and
-.codn flip .
+The place may also be deleted, which has the effect of removing the element
+from the sequence, shifting the elements at higher indices, if any, down one
+element position, and shortening the sequence by one.
.meIP >> [ sequence << from-index..to-below-index ]
Retrieve the specified range of elements.
@@ -10744,40 +11090,54 @@ for which the
(dotdot) syntactic sugar is useful.
See the section on Range Indexing below.
-The following equivalences hold:
-
-.cblk
- [seq from..to] <--> (sub seq from to)
-
- (set [seq from..to] new) <--> (refset seq new from to)
-.cble
-
-This form does not accept update operators like
-.code inc
-and
-.codn flip .
+This form is also a syntactic place, if the
+.meta sequence
+subform is a place. Storing a value in this place
+has the effect of replacing the subsequence with
+a new subsequence. Deleting the place has the
+effect of removing the specified subsequence
+from
+.metn sequence .
+The
+.meta new-value
+argument in a range assignment can be a string, vector or list,
+regardless of whether the target is a string, vector or list.
+If the target is a string, the replacement sequence must be
+a string, or a list or vector of characters.
.meIP >> [ sequence << index-list ]
Elements specified
by
-.meta index-list
+.metn index-list ,
+which may be a list or vector,
are extracted from
.meta sequence
and returned as a sequence
of the same kind as
.metn sequence .
+
This form is equivalent to
.cblk
.meti (select < sequence << where-index )
.cble
except when the target of an assignment operation.
+This form is a syntactic place if
+.meta sequence
+is one. If a sequence is assigned to this place,
+then elements of the sequence are distributed to the
+specified locations.
+
The following equivalences hold between index-list-based indexing
and the
.code select
and
.code replace
-functions:
+functions, except that
+.code set
+always returns the value assigned, whereas
+.code replace
+returns its first argument:
.cblk
[seq idx-list] <--> (select seq idx-list)
@@ -10785,11 +11145,6 @@ functions:
(set [seq idx-list] new) <--> (replace seq new idx-list)
.cble
-This form does not accept update operators like
-.code inc
-and
-.codn flip .
-
Note that unlike the select function, this does not support
.cblk
.meti >> [ hash << index-list ]
@@ -10799,46 +11154,28 @@ indistinguishable from a simple hash lookup where
.meta index-list
is the key.
-.meIP >> [ hash < key << default-value ]
+.meIP >> [ hash < key <> [ alt ]]
Retrieve a value from the hash table corresponding to
.metn key ,
-or
-.meta default-value
-if there is no such entry.
-
-The first argument may not be an operator such as
-.codn let ,
-only a function.
-
-The places denoted by the
-.code dwim
-operator can be assigned. There are some
-restrictions. List, string and vector ranges can only be replaced using
-the set operator. The other operators like
-.code push
-do not apply.
-Characters in a string can only be assigned with
-.code set
-or incremented with
-.code inc
-and dec.
-
-The source of a range assignment can be a string, vector or list,
-regardless of whether the target is a string, vector or list.
-If the target is a string, the replacement sequence must be
-a string, or a list or vector of characters.
+or else return
+.meta alt
+if there is no such entry. The expression
+.meta alt
+is always evaluated, whether or not its value is used.
.RE
.PP
-.TP* "Range Indexing"
-Vector and list range indexing is based from zero. The first element element
-zero. Furthermore, the value
+.TP* "Range Indexing:"
+Vector and list range indexing is based from zero, meaning
+that the first element is numbered zero, the second one
+and so on.
+zero. Negative values are allowed; the value
.code -1
refers to the last element of the vector or
list, and
.code -2
-to the second last and so forth. So the range
+to the second last and so forth. Thus the range
.code 1 .. -2
means
"everything except for the first element and the last two".
@@ -10925,31 +11262,43 @@ and the
.code dwim/[...]
syntax does exactly this.
-However, Lisp-2 also has its advantages, whereas Lisp-1 also has disadvantages.
-Lisp-2 integrates with macros and operators more naturally than Lisp-1. Macros
-come in two flavors: symbol macros and operator macros. This distinction
-integrates better into a two namespace model, in which an operator macro is
-recognized in the first position of a form, and a symbol macro in other
-positions. Moreover, operators and operator macros are not functions; they
-cannot work by reducing a symbol to a macro function under ordinary
-evaluation rules. A Lisp-1 implementation must inspect the leftmost expression
-to determine whether or not it is an operator, which breaks the principle
-that all expressions are to be evaluated in the same way. Moreover, macros
-and operators are never indirected upon. If
-.code x
-is an operator or macro, it never makes sense for
-.code x
-to occur other than in the leftmost position of a form. In Lisp-1 dialects,
-the situation of using the name of an operator as a variable other than
-in the first position, as in
-.code (list let if)
-has no meaning, indicating that the single namespace model serves no purpose
-for macros and operators.
-
-\*(TL banishes operators and macros (except symbol macros) from Lisp-1
-evaluation, thereby achieving a more pure model of Lisp-1 evaluation than
-actual Lisp-1 languages, while retaining all the advantages of Lisp-2.
-In short, a "best of both worlds" situation is achieved in \*(TL.
+\*(TL is a Lisp-2 because Lisp-2 also has advantages. Lisp-2 programs
+which use macros naturally achieve hygiene because lexical variables do
+not interfere with the function namespace. If a Lisp-2 program has
+a local variable called
+.codn list ,
+this does not interfere with the hidden use of the function
+.code list
+in a macro expansion in the same block of code. Lisp-1 dialects have to
+provide hygienic macro systems to attack this problem. Furthermore, even when
+not using macros, Lisp-1 programmers have to avoid using the names of functions
+as lexical variable names, if the enclosing code might use them.
+
+The two namespaces of a Lisp-2 also naturally accommodate symbol macros and
+operator macros. Whereas functions and variables can be represented in a
+single namespace readily, because functions are data objects, this is not so
+with symbol macros and operator macros, the latter of which are distinguished
+syntactically by their position in a form. In a Lisp-1 dialect, given
+.codn (foo bar) ,
+either of the two symbols could be a symbol macro, but only
+.code foo
+can possibly be an operator macro. Yet, having only a single namespace, a
+Lisp-1 doesn't permit
+.codn (foo foo) ,
+where
+.code foo
+is simultaneously a symbol macro and an operator macro, though the situation is
+unambiguous by syntax even in Lisp-1. In other words, Lisp-1 dialects do not
+entirely remove the special syntactic recognition given to the leftmost
+position of a compound form, yet at the same time they prohibit
+the user from taking full advantage of it by providing only one namespace.
+
+\*(TL provides
+the "best of both worlds": the DWIM brackets notation provides a model of
+Lisp-1 computation that is purer than Lisp-1 dialects (since the leftmost
+argument is not given any special syntactic treatment at all)
+while the Lisp-2 foundation provides a traditional Lisp environment with its
+"natural hygiene".
.coNP Function @ identity
.synb
@@ -11075,191 +11424,396 @@ In the future,
will be able to recognize more constant forms, such as calls to certain
functions whose arguments are constant forms.
-.SS* Mutation
-
-.coNP Operators @, inc @, dec @, set @, push @, pop @, flip @, zap and @ del
+.SS* Mutation of Syntactic Places
+.coNP Macro @ set
.synb
-.mets (inc < place <> [ delta ])
-.mets (dec < place <> [ delta ])
-.mets (set < place << new-value )
-.mets (push < item << place )
-.mets (pop << place )
-.mets (flip << place )
-.mets (zap < place <> [ new-value ])
-.mets (del << place )
-.syne
+.mets (set >> { place << new-value }*)
.desc
-These destructive operators update the value of a place. A place is a storage
-location which is denoted by a form. Place forms are identical to value
-accessing forms. That is to say, any form recognized as a place by these
-operators can be evaluated by itself to retrieve the value of the storage
-location. However, the converse is false: not all forms which access storage
-location are recognized as places.
-
-With are exceptions noted below, it is an error if a place does not exist.
-For instance, a variable being assigned must exist.
+The
+.code set operator stores the values of expressions in places. It must
+be given an even number of arguments.
-Literal objects which are directly specified in the source code are
-considered part of the program body. Modifying parts of these objects
-therefore gives rise to self-modifying code. The behavior of self-modifying
-code is not specified.
+If there are no arguments, then
+.code set
+does nothing and returns
+.codn nil .
-The
-.code inc
+If there are two arguments,
+.meta place
and
-.code dec
-update the place by adding or subtracting, respectively, a
-displacement to or from that number. If the
-.meta delta
-expression is
-specified, then it is evaluated and its value is used as the increment.
-Otherwise, a default increment of
-.code 1
-is used. The prior value of the place
-and the delta must be suitable operands for the
-.code +
-and
-.code -
-functions.
-.code (inc x)
+.metn new-value ,
+then
+.meta place is evaluated to determine its storage location,
+then
+.meta new-value
+is evaluated to determine the value to be stored there,
+and then the value is stored in that location. Finally,
+the value is also returned as the result value.
+
+If there are more than two arguments, then
+.code
+set performs multiple assignments in left to right order.
+Effectively,
+.code (set v1 e1 v2 e2 ... vn en)
+is precisely equivalent to
+.codn (progn (set v1 e1) (set v2 e2) ... (set vn en)) .
+
+.coNP Macro @ pset
+.synb
+.mets (pset >> { place << new-value }*)
+.syne
+.desc
+The syntax of
+.code pset
+is similar to that of
+.codn set ,
+and the semantics is similar also in that zero or more places are
+assigned zero or more values. In fact, if there are no arguments, or
+if there is exactly one pair of arguments,
+.code pset
is equivalent to
-.codn (set x (+ 1 x)) ,
-except that expression x
-is evaluated only once to determine the storage location. The
-.code inc
-and
-.code dec
-operators return the new value that was stored.
+.codn set .
-The
-.code set
-operator overwrites the previous value of a place with a new value,
-and also returns that value.
+If there are two or more argument pairs, then all of the arguments
+are evaluated first, in left-to-right order. No store takes place
+until after every
+.meta place
+is determined, and every
+.meta new-value
+is calculated. During the calculation, the values to be stored
+are retained in hidden, temporary locations. Finally, these values
+are moved into the determined places. The rightmost value is returned
+as the form's value.
-The
-.code push
+The assignments thus appear to take place in parallel, and
+.code pset
+is capable of exchanging the values of a pair of places, or rotating
+the values among three or more places. (However, there are more convenient
+operators for this, namely
+.code rotate
and
-.code pop
-operators operate on a place which holds a list. The
-.code push
-operator updates the list by replacing it with a new list which has a new item
-at the front, followed by the previous list. The item is returned.
-The
-.code pop
-operator performs the reverse operation: it removes the first item
-from the list and returns it.
-.code (push y x)
-is similar to
+.codn swap ).
+.TP* Example:
.cblk
- (let ((temp y)) (set x (cons temp x)) temp)
+ ;; exchange x and y
+ (pset x y y x)
+
+ ;; exchange elements 0 and 1; and 2 and 3 of vector v:
+ (let ((v (vec 0 10 20 30))
+ (i -1))
+ (pset [vec (inc i)] [vec (inc i)]
+ [vec (inc i)] [vec (inc i)])
+ vec)
+ -> #(10 0 30 20)
.cble
-except that
-.code x
-is evaluated only once to determine the storage place, and no
-such temporary variable is visible to the program. Similarly,
-.code (pop x)
-is much like
+.coNP Macro @ zap
+.synb
+.mets (zap < place <> [ new-value ])
+.syne
+.desc
+The
+.code zap
+macro assigns
+.meta new-value
+to
+.meta place
+and returns the previous value of
+.metn place .
-.cblk
- (let ((temp (car x))) (set x (cdr x)) temp)
-.cble
+If
+.meta new-value
+is missing, then
+.code nil
+is used.
+
+In more detail, first
+.code place
+is evaluated to determine the storage location.
+Then, the location is accessed to retrieve the
+previous value. Then, the
+.code new-value
+expression is evaluated, and that value is
+placed into the storage location.
+Finally, the previously retrieved value is returned.
-except that x is evaluated only once, and no such temporary variable
-is visible to the program.
+.coNP Macro @ flip
+.synb
+.mets (flip << place )
+.syne
+.desc
The
.code flip
-operator toggles a place between true and false. If the place
-contains a value other than
+macro toggles the boolean value stored in
+.metn place .
+
+If
+.meta place
+previously held
.codn nil ,
-then its value is replaced with
-.codn nil .
-If it contains
+it is set to
+.codn t ,
+and if it previously held a value other than
.codn nil ,
-it is replaced with
-.codn t .
+it is set to
+.codn nil .
+.coNP Macros @ inc and @ dec
+.synb
+.mets (inc < place <> [ delta ])
+.mets (dec < place <> [ delta ])
+.syne
+.desc
The
-.code zap
-operator is similar to
-.code set
-except that
-.meta new-value
-is optional, defaulting to
-.codn nil ,
-and the operator returns the previous value of the place.
-This operator is useful in avoiding spurious retention of memory in the
-face of garbage collection. The idiom
-.code (function (zap variable))
-passes the value of
-.code variable
-to
-.code function
-while at the same time clearing it to
-.codn nil . This means that the caller no longer has a reference to the
-contents of the variable as far as the garbage collector is concerned.
-This approach is essential in some uses of infinite, lazy objects.
+.code inc
+macro increments
+.meta place
+by adding
+.meta delta
+to its value.
+If
+.meta delta
+is missing, the value used in its place the integer 1.
+
+First the
+.meta place
+argument is evaluated as a syntactic place to determine the location.
+Then, the value currently stored in that location is retrieved.
+Next, the
+.meta delta
+expression is evaluated. Its value is added to the previously retrieved
+value as if by the
+.code +
+function. The resulting value is stored in the place, and returned.
+The macro
+.code dec
+works exactly like
+.code inc
+except that addition is replaced by subtraction. The similarly defaulted
+.meta delta
+value is subtracted from the previous value of the place.
+
+.coNP Macro @ swap
+.synb
+.mets (swap < left-place << right-place )
+.syne
+.desc
The
-.code del
-operator does not modify the value of a place, but rather deletes the
-place itself. Index values and ranges of lists denoted using the
-.code dwim
-operator
-indexing notation can be subject to a deletion, as can hash table entries
-denoted using
-.code dwim
-or
-.codn gethash .
-It is an error to try to delete other kinds of
-places such as simple variables. The
-.code del
-operator returns the value of the
-place that was deleted. Deleting from a sequence means removing the element or
-elements. Deleting a hash place means removing the corresponding entry from the
-hash table.
+.code swap
+macro exchanges the values of
+.meta left-place
+and
+.meta right-place
+and returns the value which is thereby transferred to
+.metn right-place .
-Currently, these forms are recognized as places:
+First,
+.meta left-place
+and
+.meta right-place
+are evaluated, in that order, to determine their locations.
+Then the prior values are retrieved, exchanged and stored back.
+The value stored in
+.meta right-place
+is also returned.
-.meIP < symbol
-A .meta symbol place denotes a variable. If the variable does not exist, it is
-an error.
+.coNP Macro @ push
+.synb
+.mets (push < item << place )
+.syne
+.desc
+The
+.code push
+macro places
+.meta item
+at the head of the list stored in
+.meta place
+and returns the updated list which is stored back in
+.metn place .
+
+First, the expression
+.meta item
+is evaluated to produce the push value.
+Then,
+.meta place
+is evaluated to determine its storage location.
+Next, the storage location is accessed to retrieve the
+list value which is stored there. A new object is
+produced as if by invoking
+.code cons
+function on the push value and list value.
+This object is stored into the location,
+and returned.
-.meIP (car << cons )
-.meIP (cdr << cons )
-These places denote the corresponding slots
-of a cons cell. The
-.meta cons
-form must be an expression which evaluates to a cons.
+.coNP Macro @ pop
+.synb
+.mets (pop << place )
+.syne
+The
+.code pop
+macro removes an element from the list stored in
+.meta place
+and returns it.
-.meIP (gethash < hash < key << default-value )
+First,
+.meta place
+is evaluated to determine the place. The place is accessed to
+retrieve the original value. Then a new value is calculated,
+as if by applying the
+.code cdr
+function to the old value. This new value is stored.
+Finally, a return value is calculated and returned, as if by applying the
+.code car
+function to the original value.
+
+.coNP Macro @ pushnew
+.synb
+.mets (pushnew < item < place >> [ testfun <> [ keyfun ]])
+.syne
+.desc
The
-.code gethash
-place denotes a value stored in a hash table.
-The form
-.meta hash
-must evaluate to a hash table. If the place does not exist
-in the hash table under the given key, then the destructive operation
-will create it. In that case, the
-.meta default-value
-form is evaluated to
-determine the initial value of the place. Otherwise it is ignored.
+.code pushnew
+macro inspects the list stored in
+.metn place .
+If the list already contains the item, then
+it returns the list. Otherwise it creates a new list
+with the item at the front and stores it back
+into
+.metn place ,
+and returns it.
+
+First, the expression
+.meta item
+is evaluated to produce the push value.
+Then,
+.meta place
+is evaluated to determine its storage location.
+Next, the storage location is accessed to retrieve the
+list value which is stored there. The list is
+inspected to check whether it already contains the push
+value, as if using the
+.code member
+function. If that is the case, the list
+is returned and the operation finishes.
+Otherwise, a new object is
+produced as if by invoking
+.code cons
+function on the push value and list value.
+This object is stored into the location
+and returned.
-.meIP (vecref < vector << index )
+.coNP Macro @ shift
+.synb
+.mets (shift << place + << shift-in-value)
+.syne
+.desc
The
-.code vecref
-place denotes a vector element, allowing vector elements
-to be treated as assignment places.
+.code shift
+macro treats one or more places as a "multi-place shift register".
+The values of the places are shifted one place to the left.
+The first (leftmost) place receives the value of the second place,
+the second receives that of the third, and so on.
+The last (rightmost) place receives
+.meta shift-in-value
+(which is not treated as a place, even if it is a syntactic place form).
+The previous value of the first place is returned.
+
+More precisely, all of the argument forms are evaluated left to right, in the
+process of which the storage locations of the places are determined,
+.meta shift-in-value
+is reduced to its value.
+
+The values stored in the places are sampled and saved.
+
+Note that it is not specified whether the places are sampled in a separate
+pass after the evaluation of the argument forms, or whether the
+sampling is interleaved into the argument evaluation. This affects
+the behavior in situations in which the evaluation of any of the
+.meta place
+forms, or of
+.metn shift-in-value ,
+has the side effect of modifying later places.
+
+Next, the places are updated by storing the saved value of the second
+place into the first place, the third place into the second and so forth,
+and the value of
+.meta shift-in-value
+into the last place.
+
+Finally, the saved original value of the first place is returned.
+
+.coNP Macro @ rotate
+.synb
+.mets (rotate << place *)
+.syne
+.desc
+Treats zero or more places as a "multi-place rotate register".
+If there are no arguments, there is no effect and
+.code nil
+is returned. Otherwise, the last (rightmost) place receives
+the value of the first (leftmost) place. The leftmost place
+receives the value of the second place, and so on.
+If there are two arguments, this equivalent to
+.codn swap .
+The prior value of the first place, which is the the value
+rotated into the last place, is returned.
+
+More precisely, the
+.meta place
+arguments are evaluated left to right,
+and the storage locations are thereby determined. The storage
+locations are sampled, and then the sampled values are
+stored back into the locations, but rotated by one place
+as described above. The saved original value of the leftmost
+.meta place
+is returned.
+
+It is not specified whether the sampling of the original values
+is a separate pass which takes place after the arguments
+are evaluated, or whether this sampling it is interleaved into argument
+evaluation. This affects
+the behavior in situations in which the evaluation of any of the
+.meta place
+forms has the side effect of modifying the value stored in
+a later
+.meta place
+form.
-.meIP (dwim < obj ...)
-.meIP >> [ obj ...] ;; equivalent
+.coNP Macro @ del
+.synb
+.mets (del << place )
+.syne
+.desc
The
-.code dwim/[]
-place denotes a vector element, list element, string, or hash
-table, depending on the type of
-.metn obj .
+.code del
+macro requests the deletion of
+.codn place .
+If
+.code place
+doesn't support deletion, an exception is thrown.
+
+First
+.code place
+is evaluated, thereby determining its location.
+Then the place is accessed to retrieve its value.
+The place is then subject to deletion. Finally, the
+previously retrieved value is returned.
+
+Precisely what deletion means depends on the kind of place.
+The built-in places in \*(TL have deletion semantics which are
+intended to be unsurprising to the programmer familiar with the
+data structure which holds the place.
+
+Generally, if a place denotes the element of a sequence, then deletion of the
+place implies deletion of the element, and deletion of the element implies that
+the gap produced by the element is closed. The deleted element is effectively
+replaced by its successor, that successor by its successor and so on. If a
+place denotes a value stored in a dynamic data set such as a hash table,
+then deletion of that place implies deletion of the entry which holds
+that value. If the entry is identified by a key, that key is also removed.
.SS* Binding and Iteration
.coNP Operators @ defvar and @ defparm
@@ -11858,10 +12412,12 @@ syntax
.code (fun (lambda ...))
is invalid.
-.coNP Functions @ symbol-function and @ symbol-value
+.coNP Accessors @ symbol-function and @ symbol-value
.synb
.mets (symbol-function << symbol )
.mets (symbol-value << symbol )
+.mets (set (symbol-function << symbol ) << new-value )
+.mets (set (symbol-value << symbol ) << new-value )
.syne
.desc
@@ -11913,6 +12469,23 @@ are currently not assignable places. Only the
.code defun
operator defines functions.
+A
+.code symbol-function
+or
+.code symbol-value
+form denotes a place. If
+.meta symbol
+has a binding in, respectively, the function or variable namespace
+of the global environment, then a value may be stored in the place,
+otherwise an error is thrown. Deleting a place doesn't require a binding;
+it takes place as if using the
+.code fmakunbound
+or
+.code makunbound
+functions. If a nonexistent place is deleted, the prior value yielded
+by the deletion is deemed to be
+.codn nil .
+
.coNP Functions @ boundp and @ fboundp
.synb
.mets (boundp << symbol )
@@ -12591,10 +13164,12 @@ and the macro
(consp '(3)) -> t
.cble
-.coNP Functions @ car and @ first
+.coNP Accessors @ car and @ first
.synb
.mets (car << object )
.mets (first << object )
+.mets (set (car << object ) << new-value )
+.mets (set (first << object ) << new-value )
.syne
.desc
The functions
@@ -12627,10 +13202,41 @@ string, then
is returned. Otherwise the first character of the string or
first element of the vector is returned.
-.coNP Functions @ cdr and @ rest
+A
+.code car
+form denotes a valid place when
+.meta object
+is accessible via
+.codn car ,
+isn't the object
+.codn nil ,
+and is modifiable.
+
+A
+.code car
+form supports deletion. The following equivalence
+then applies:
+
+.cblk
+ (del (car place)) <--> (pop place)
+.cble
+
+This implies that deletion requires the argument of the
+.code car
+form to be a place, rather than the whole form itself.
+In this situation, the argument place may have a value
+which is
+.codn nil ,
+because
+.code pop
+is defined on an empty list.
+
+.coNP Accessors @ cdr and @ rest
.synb
.mets (cdr << object )
.mets (rest << object )
+.mets (set (cdr << object ) << new-value )
+.mets (set (rest << object ) << new-value )
.syne
.desc
The functions
@@ -12640,7 +13246,7 @@ and
are synonyms.
If
-.meta obejct
+.meta object
is a cons cell, these functions retrieve the
.code cdr
field of that cons cell.
@@ -12675,6 +13281,33 @@ and
both result in
.codn nil .
+The invocation syntax of a
+.code cdr
+or
+.code rest
+form is a syntactic place. The place is semantically valid when
+.meta object
+is accessible via
+.codn cdr ,
+isn't the object
+.codn nil ,
+and is modifiable.
+
+A
+.code cdr
+place supports deletion, according to the following equivalence:
+
+.cblk
+ (del (cdr obj)) <--> (zap (cdr obj))
+.cble
+
+Note that this is different from the delete semantics of
+.code car
+in that
+.code obj
+is not required to be a place, and must not be
+.codn nil .
+
.TP*
Example:
@@ -12729,7 +13362,8 @@ and
.code cdr
fields of the cell
.metn cons .
-Note that
+
+Note that, except for the difference in return value,
.code (rplaca x y)
is the same as the more generic
.codn (set (car x) y) ,
@@ -12753,10 +13387,38 @@ and
.code rplacd
functions return
.metn cons .
-
Note: \*(TX versions 89 and earlier, these functions returned the new value.
The behavior was undocumented.
+The
+.meta cons
+argument does not have to be a cons cell. Both functions support meaningful
+semantics for vectors and strings. If
+.meta cons
+is a string, it must be modifiable.
+
+The
+.code rplaca
+function replaces the first element of a vector or first character
+of a string. The vector or string must be at least one element long.
+
+The
+.code rplacd
+function replaces the suffix of a vector or string after the first element
+with a new suffix. The
+.meta new-cdr-value
+must be a sequence, and if the suffix of a string is being replaced,
+it must be a sequence of characters. The suffix here refers to the portion of
+the vector or string after the first element.
+
+It is permissible to use
+.code rplacd
+on an empty string or vector. In this case,
+.meta new-cdr-value
+specifies the contents of the entire string or vector, as if the operation
+were done on a non-empty vector or string, followed by the deletion of the
+first element.
+
.coNP Functions @, second @, third @, fourth @ fifth and @ sixth
.synb
.mets (first << list )
@@ -12956,7 +13618,7 @@ The
function is like the replace function, except that the first
argument must be a list.
-For a description of the arguments and semantics, refer to the
+For a description of the arguments, semantics and return value, refer to the
.code replace
function.
@@ -15605,9 +16267,10 @@ only retrieve a cached value.
*list* is (2 3)
.cble
-.coNP Function @ force
+.coNP Accessor @ force
.synb
.mets (force << promise )
+.mets (set (force << promise ) << new-value )
.syne
.desc
The
@@ -15638,6 +16301,30 @@ function call. If the
function is invoked additional times on
the same promise, the cached value is retrieved.
+A
+.code force
+form is a syntactic place, denoting the value cache location within
+.metn promise .
+
+Storing a value in a
+.code force
+place causes future accesses to the
+.meta promise
+to return that value.
+
+If the promise had not yet been forced, then
+storing a value into it prevents that from ever happening. The
+delayed
+.meta expression
+will never be evaluated.
+
+If, while a promise is being forced, the evaluation of
+.meta expression
+itself causes an assignment to the promise, it is not specified whether
+the promise will take on the value of
+.meta expression
+or the assigned value.
+
.coNP Macro @ mlet
.synb
.mets (mlet >> ({ sym | >> ( sym << init-form )}*) << body-form *)
@@ -16593,7 +17280,7 @@ function is like the
function, except that the first
argument must be a string.
-For a description of the arguments and semantics, refer to the
+For a description of the arguments, semantics and return value, refer to the
.code replace
function.
@@ -17108,9 +17795,10 @@ to
The argument is taken to be a Unicode code point value and the
corresponding character object is returned.
-.coNP Function @ chr-str
+.coNP Accessor @ chr-str
.synb
.mets (chr-str < str << idx )
+.mets (set (chr-str < str << idx ) << new-value )
.syne
.desc
The
@@ -17134,6 +17822,25 @@ An empty string cannot be indexed. A string of length one supports index 0 and
index -1. A string of length two is indexed left to right by the values 0 and
1, and from right to left by -1 and -2.
+If the element
+.meta idx
+of string
+.meta str
+exists, and the string is modifiable, then the
+.code chr-str
+form denotes a place.
+
+A
+.code chr-str
+place
+supports deletion. When a deletion takes place,
+then the character at
+.meta idx
+is removed from the string. Any characters
+after that position move by one position
+to close the gap, and the length of the string
+decreases by one.
+
.TP* Notes:
Direct use of
@@ -17155,6 +17862,16 @@ this also holds:
(chr-str s i) --> (ref s i)
.cble
+However, note the following difference. When the expression
+.code [s i]
+is used as a place, then the subexpression
+.code s
+must be a place. When
+.code (chr-str s i)
+is used as a place,
+.code s
+need not be a place.
+
.coNP Function @ chr-str-set
.synb
.mets (chr-str-set < str < idx << char )
@@ -17636,9 +18353,10 @@ argument must be nonnegative.
The return value is
.metn vec .
-.coNP Function @ vecref
+.coNP Accessor @ vecref
.synb
.mets (vecref < vec << idx )
+.mets (set (vecref < vec << idx ) << new-value )
.syne
.desc
The
@@ -17654,6 +18372,32 @@ The
value must range from 0 to one less than the
length of the vector. The specified element is returned.
+If the element
+.meta idx
+of vector
+.meta vec
+exists, then the
+.code vecref
+form denotes a place.
+
+A
+.code vecref place
+supports deletion. When a deletion takes place,
+then if
+.meta idx
+denotes the last element in the vector, the
+vector's length is decreased by one, so that
+the vector no longer has that element.
+Otherwise, if
+.meta idx
+isn't the last element, then each elements
+values at a higher index than
+.meta idx
+shifts by one one element position to the
+adjacent lower index. Then, the length of the
+vector is decreased by one, so that the last
+element position disappears.
+
.coNP Function @ vec-push
.synb
.mets (vec-push < vec << elem )
@@ -17768,7 +18512,7 @@ is like the
function, except that the first argument
must be a vector.
-For a description of the arguments and semantics, refer to the
+For a description of the arguments, semantics and return value, refer to the
.code replace
function.
@@ -17966,6 +18710,46 @@ with the input sequence.
.syne
.desc
The
+.meta replace
+function modifies
+.meta sequence
+in the ways described below.
+
+The operation is destructive: it may work "in place" by modifying
+the original sequence. The caller should retain the return value
+and stop relying on the original input sequence.
+
+The return value of
+.code replace
+is the modified
+version of
+.metn sequence .
+This may be the same object as
+.meta sequence
+or it may be a newly allocated object.
+
+Note that the form:
+
+.cblk
+ (set seq (replace seq new fr to))
+.cble
+
+has the same effect on the variable
+.code seq
+as the form:
+
+.cblk
+ (set [seq fr..to] new)
+.cble
+
+except that the former
+.code set
+form returns the entire modified sequence, whereas the latter
+returns the value of the
+.code new
+argument.
+
+The
.code replace
function has two invocation styles, distinguished by the
type of the third argument. If the third argument is a list or vector, then it
@@ -18000,20 +18784,9 @@ and
.code t
respectively.
-The following equivalence holds between assignment to a place denoted by
-DWIM bracket syntax and first form of the replace function:
-
-.cblk
- (set seq (replace seq new fr to)) <--> (set [seq fr..to] new)
-.cble
-
The description of the dwim operator\(emin particular, the section
on Range Indexing\(emexplains the semantics of the range specification.
-This operation is destructive: it may work "in place" by modifying
-the original sequence. The caller should retain the return value
-and stop relying on the original input sequence.
-
The second form of the replace function replaces a subsequence of
elements from
.meta sequence
@@ -18044,15 +18817,6 @@ is a list, then
must
be monotonically increasing.
-The following equivalence holds between assignment to a place denoted by
-DWIM bracket syntax and second form of the
-.code replace
-function:
-
-.cblk
- (set seq (replace seq new ix-list)) <--> (set [seq ix-list] new)
-.cble
-
.coNP Function @ search
.synb
.mets (search < haystack < needle >> [ testfun <> [ keyfun ])
@@ -20938,9 +21702,10 @@ the hash. Modifying the
.code cdr
field has the effect of updating the association with a new value.
-.coNP Function @ gethash
+.coNP Accessor @ gethash
.synb
.mets (gethash < hash < key <> [ alt ])
+.mets (set (gethash < hash < key <> [ alt ]) << new-value )
.syne
.desc
The
@@ -20960,6 +21725,41 @@ was not specified,
.code nil
is returned.
+A valid
+.code gethash
+form serves as a place. It denotes either an existing value in a hash
+table or a value that would be created by the evaluation of the form.
+The
+.meta alt
+argument is meaningful when
+.code gethash
+is used as a place, and, if present, is always evaluated whenever the place is
+evaluated.
+In place update operations, it provides the initial value, which defaults
+to
+.code nil
+if the argument is not specified. For example
+.code (inc (gethash h k d))
+will increment the value stored under key
+.code k
+in hash table
+.code h
+by one. If the key does not exist in the hash table, then
+the value
+.code (+ 1 d)
+is inserted into the table under that key.
+The expression
+.code d
+is always evaluated, whether or not its value is needed.
+
+If a
+.code gethash
+place is subject to a deletion, but doesn't exist, it is not an error.
+The operation does nothing, and
+.code nil
+is considered the prior value of the place yielded
+by the deletion.
+
.coNP Function @ sethash
.synb
.mets (sethash < hash < key << value )
@@ -20982,6 +21782,12 @@ pair is
newly inserted into
.metn hash .
+The
+.code sethash
+function returns the
+.metn value
+argument.
+
.coNP Function @ pushhash
.synb
.mets (pushhash < hash < key << element )
@@ -21010,6 +21816,10 @@ value is added to the front of that list,
and the extended list then becomes the new value under
.metn key .
+The return value is boolean. If true, indicates that the hash table entry was
+newly created. If false, it indicates that the push took place on an existing
+entry.
+
.coNP Function @ remhash
.synb
.mets (remhash < hash << key )
@@ -21024,6 +21834,11 @@ for a key similar to the
If that key is found, then that key and its corresponding value are
removed from the hash table.
+If the key is found and removal takes place, then the associated value
+is returned. Otherwise
+.code nil
+is returned.
+
.coNP Function @ hash-count
.synb
.mets (hash-count << hash )
@@ -24529,9 +25344,10 @@ if it exists. On some platforms, it instead sets the environment variable
to the empty string.
.SS* System Programming
-.coNP Function @ errno
+.coNP Accessor @ errno
.synb
.mets (errno <> [ new-errno ])
+.mets (set (errno) << new-value )
.syne
.desc
The
@@ -24547,6 +25363,10 @@ specifies a value which is stored into
.codn errno .
The value returned is the prior value.
+The place form of
+.code errno
+does not take an argument.
+
.coNP Function @ exit
.synb
.mets (exit << status )
@@ -27051,19 +27871,9 @@ The return value of the macro is the macro expansion. It is substituted in
place of the entire macro call form. That form is then expanded again;
it may itself be another macro call, or contain more macro calls.
-.TP* Examples:
+.TP* Example:
.cblk
- ;; A macro to swap two places, such as variables.
- ;; (swap x y) will now exchange the contents
- ;; of x and y.
-
- (defmacro swap (a b)
- (let ((temp (gensym)))
- ^(let ((,temp ,a))
- (set ,a ,b)
- (set ,b ,temp))))
-
;; dolist macro similar to Common Lisp's:
;;
;; The following will print 1, 2 and 3
@@ -27667,6 +28477,1227 @@ should be understood to be a globally unique symbol:
clause1 clause2 ...))
.cble
+.SS* User-Defined Places and Place Operators
+
+\*(TL provides a number of place-modifying operators such as
+.codn set ,
+.codn push ,
+and
+.codn inc .
+It also provides a variety of kinds of syntactic places
+which may be used with these operators.
+
+Both of these categories are open-ended: \*(TL programs may extend
+the set of place-modifying operators, as well as the vocabulary of
+forms which are recognized as syntactic places.
+
+Regarding place operators, it might seem obvious that new place operators can
+be developed, since they are macros, and macros can expand to uses
+of existing place operators. As an example, it may seem that
+.code inc
+operator could be written as a macro which uses
+.codn set :
+
+.cblk
+ (defmacro new-inc (place : (delta 1))
+ ^(set ,place (+ ,place ,delta)))
+.cble
+
+However, the above
+.code new-inc
+macro has a problem: the
+.code place
+argument form is inserted into two places in the expansion, which
+leads to two evaluations. This is visibly incorrect if the place
+form contains any side effects. It is also potentially inefficient.
+
+\*(TL provides a framework for writing place update macros which
+evaluate their argument forms once, even if they have to access
+and update the same places.
+
+The framework also supports the development of new kinds of place forms
+as capsules of code which introduce the right kind of material into
+the lexical environment of the body of an update macro, to enable
+this special evaluation.
+
+.NP* Place-Expander Functions
+
+The central design concept in \*(TL syntactic places are place-expander
+function. Each compound place is defined by up to three
+.IR "place-expander functions" ,
+which are associated with the place via the leftmost operator
+symbol of the place form. One place-expander, the
+.IR "update expander" ,
+is mandatory. Optionally, a place may also provide a
+.I "clobber expander"
+as well as a
+.IR "delete expander" .
+An update expander provides the expertise for evaluating a place form once
+in its proper run-time context to determine its actual run-time storage
+location, and to access and modify the storage location.
+A clobber expander provides an optimized mechanism for uses that perform
+a one-time store to a place without requiring its prior value.
+If a place definition does not supply a clobber expander, then the syntactic
+places framework uses the update expander to achieve the functionality.
+A delete expander provides the expertise for determining the actual run-time
+storage location corresponding to a place, and obliterating it,
+returning its prior value. If a place does not supply a delete expander, then
+the place does not support deletion. Operators which require deletion, such as
+.code del
+will raise an error when applied to that place.
+
+The expanders operate independently, and it is expected that place-modifying
+operators choose one of the three, and use only that expander. For example,
+accessing a place with an update expander and then overwriting its value
+with a clobber expander may result in incorrect code which contains multiple
+evaluations of the place form.
+
+The programmer who implements a new place does not write expanders directly,
+but rather defines them via the
+.code defplace
+macro.
+
+The programmer who implements a new place update macro likewise does not
+call the expanders directly. Usually, they are invoked via the macros
+.codn with-update-expander ,
+.codn with-clobber-expander
+and
+.codn with-delete-expander .
+These are sufficient for most kind of macros.
+In certain complicated cases, expanders may be invoked using the wrapper
+functions
+.codn call-update-expander ,
+.codn call-clobber-expander
+and
+.codn call-delete-expander .
+These convenience macros and functions perform certain common chores, like
+macro-expanding the place in the correct environment, and choosing the
+appropriate function.
+
+The expanders are described in the following sections.
+
+.NP* The Update Expander
+.synb
+.mets (lambda >> ( getter-sym < setter-sym < place-form
+.mets \ \ \ \ \ \ \ \ << body-form ) ...)
+.syne
+.desc
+The update expander is a code-writer. It takes a
+.meta body-form
+argument, representing code, and returns a larger form which surrounds
+this code with additional code.
+
+This larger form returned by the update expander can be regarded as having two
+abstract actions, when it is substituted and evaluated in the context where
+.meta place-form
+occurs. The first abstract action is to evaluate
+.meta place-form
+exactly one time, in order to determine the actual run-time location to which
+that form refers.
+The second abstract action is to evaluate the caller's
+.metn body-form -s,
+in a lexical environment in which bindings exist for some lexical
+functions or (more usually) lexical macros. These lexical macros
+are explicitly referenced by the
+.metn body-form ;
+the update expander just provides their definition, under the names
+it is given via the
+.meta getter-sym
+and
+.meta setter-sym
+arguments.
+
+The update expander writes local functions or macros under these names: a
+getter function and a setter function. Usually, update expanders write
+macros rather than functions, possibly in combination with some lexical
+anonymous variables which hold temporary objects. Therefore the getter
+and setter are henceforth referred to as macros.
+
+The code being generated is with regard to some concrete instance of
+.metn place-form .
+This argument is the actual form which occurs in a program. For
+instance, the update expander for the
+.code car
+place might be called with an arbitrary variant of the
+.meta place-form
+might look like
+.codn (car (inc (third some-list))) .
+
+In the abstract semantics, upfront code wrapped around the
+.meta body-form
+by the update expander provides the logic to evaluate this place to
+a location, which is retained in some hidden local context.
+
+The getter local macro named by
+.meta getter-sym
+must provide the logic for retrieving the value of this place.
+The getter macro takes no arguments.
+The
+.meta body-form
+makes free use of the getter function; they may call it multiple times,
+which must not trigger multiple evaluations of the original place
+form.
+
+The setter local macro named by
+.meta setter-sym
+must generate the logic for storing a new value into the once-evaluated
+version of
+.metn place-form .
+The setter function takes exactly one argument, whose
+value specifies the value to be stored into the place.
+It is the caller's responsibility to ensure that the
+argument form which produces the value to be stored via the setter is evaluated
+only once, and in the correct order. The setter does not concern itself with
+this form. Multiple calls to the setter can be expected to result in multiple
+evaluations of its argument. Thus, if necessary, the caller must supply the code
+to evaluate the new value form to a temporary variable, and then pass the
+temporary variable to the setter. This code can be embedded in
+the
+.meta body-form
+or can be added to the code returned by a call to the update expander.
+
+The setter local macro or function must return the new value which is stored.
+That is to say, when
+.meta body-form
+invokes this local macro or function, it may rely on it yielding the
+new value which was stored, as part of achieving its own semantics.
+
+The update expander does not macro-expand
+.codn place-form .
+It is assumed that the expander is invoked in such a way that the
+place has been expanded in the correct environment. In other words, the
+form matches the type of place which the expander handles.
+If the expander had to macro-expand the place form, it would sometimes have
+to come to the conclusion that the place form must be handled by a different
+expander. No such consideration is the case: when an expander is called on
+a form, that is final; it is certain that it is the correct expander, which
+matches the symbol in the
+.code car
+position of the form, which is not a macro in the context where it occurs.
+
+An update expander is free to assume that any place which is stored
+(the setter local macro is invoked on it) is accessed at least once by
+an invocation of the getter. A place update macro which relies on an update
+expander, but uses only the store macro, might not work properly.
+An example of an update expander which relies on this assumption is the
+expander for the
+.cblk
+.meti (force << promise )
+.cble
+place type. If
+.meta promise
+has not yet been forced, and only the setter is used, then
+.meta promise
+might remain unforced as its internal value location is updated.
+A subsequent access to the place will incorrectly trigger a force,
+which will overwrite the value. The expected behavior is that storing
+a value in an unforced
+.code force
+place changes the place to forced state, preempting the evaluation of
+the delayed form. Afterward, the promise exhibits the value which was
+thus assigned.
+
+The update expander is not responsible for all issues of evaluation order. A
+place update macro may consist of numerous places, as well as numerous
+value-producing forms which are not places. Each of the places can provide its
+registered update expander which provides code for evaluating just that place,
+and a means of accessing and storing the values. The place update macro must
+call the place expanders in the correct order, and generate any additional code
+in the correct order, so that the macro achieves its required documented
+evaluation order.
+
+.TP* "Example Update Expander Call:"
+
+.cblk
+ ;; First, capture the update expander
+ ;; function for (car ...) places
+ ;; in a variable, for clarity.
+
+ (defvar car-update-expander [*place-update-expander* 'car])
+
+ ;; Next, call it for the place (car [a 0]).
+ ;; The body form specifies logic for
+ ;; incrementing the place by one and
+ ;; returning the new value.
+
+ (call car-update-expander 'getit 'setit '(car [a 0])
+ '(setit (+ (getit) 1)))
+
+ --> ;; Resulting code:
+
+ (rlet ((#:g0032 [a 0]))
+ (macrolet ((getit nil
+ (append (list 'car) (list '#:g0032)))
+ (setit (val)
+ (append (list 'sys:rplaca)
+ (list '#:g0032) (list val))))
+ (setit (+ (getit) 1))))
+
+ ;; Same expander call as above, with a sys:expand to show the
+ ;; fully expanded version of the returned code, in which the
+ ;; setit and getit calls have disappeared, replaced by their
+ ;; macro-expansions.
+
+ (sys:expand
+ (call car-update-expander 'getit 'setit '(car [a 0])
+ '(setit (+ (getit) 1))))
+
+ -->
+
+ (let ((#:g0032 [a 0]))
+ (sys:rplaca #:g0032 (+ (car #:g0032) 1)))
+
+.cble
+The main noteworthy points about the generated code are:
+.RS
+.IP -
+the
+.code (car [a 0])
+place is evaluated by evaluating the embedded form
+.code [a 0]
+and storing storing the resulting object into a hidden local variable.
+That's as close a reference as we can make to the
+.code car
+field.
+.IP -
+the getter macro expands to code which simply calls the
+.code car
+function on the cell.
+.IP -
+the setter uses a system function called
+.codn sys:rplaca ,
+which differs from
+.code rplaca
+in that it returns the stored value, rather than the cell.
+.RE
+
+.NP* The Clobber Expander
+.synb
+.mets (lambda >> ( simple-setter-sym < place-form
+.mets \ \ \ \ \ \ \ \ << body-form ) ...)
+.syne
+.desc
+The clobber expander is a code-writer similar to the update expander.
+It takes a
+.meta body-form
+argument, and returns a larger form which surrounds this form
+with additional program code.
+
+The returned block of code has one main abstract action.
+It must arrange for the evaluation of
+.meta body-form
+in a lexical environment in which a lexical macro or lexical function
+exists which has the name requested by the
+.meta simple-setter-sym
+argument.
+
+The simple setter local macro written by the clobber expander is similar to the
+local setter written by the update expander. It has exactly the
+same interface, performs the same action of storing a value into
+the place, and returns the new value.
+
+The difference is that its logic may be considerably simplified by the
+assumption that the place is being subject to exactly one store,
+and no access.
+
+A place update macro which uses a clobber expander, and calls it more than
+once, break the assumption; doing so may result in multiple evaluations
+of the
+.metn place-form .
+
+.NP* The Delete Expander
+.synb
+.mets (lambda >> ( deleter-sym < place-form
+.mets \ \ \ \ \ \ \ \ << body-form ) ...)
+.syne
+.desc
+The delete expander is a code-writer similar to clobber expander.
+It takes a
+.meta body-form
+arguments, and returns a larger form which surrounds this form
+with additional program code.
+
+The returned block of code has one main abstract action.
+It must arrange for the evaluation of
+.meta body-form
+in a lexical environment in which a lexical macro or lexical function
+exists which has the name requested by the
+.meta deleter-sym
+argument.
+
+The deleter macro written by the clobber expander takes no arguments.
+It may be called at most once. It returns the previous value of the
+place, and arranges for its obliteration, whatever that means for
+that particular kind of place.
+
+.coNP Macro @ with-update-expander
+.synb
+.mets (with-update-expander >> ( getter << setter ) < place < env
+.mets \ << body-form )
+.syne
+.desc
+The
+.code with-update-expander
+macro evaluates the
+.meta body-form
+argument, whose result is expected to be a Lisp form.
+The macro adds additional code around this code, and the result is returned.
+This additional code is called the
+.IR "place-access code" .
+
+The
+.meta getter
+and
+.meta setter
+arguments must be symbols. Over the evaluation of the
+.metn body-form ,
+these symbols are bound to the names of local functions which
+are provided in the place-access code.
+
+The
+.meta place
+argument is a form which evaluates to a syntactic place. The generated
+place-access code is based on this place.
+
+The
+.meta env
+argument is a form which evaluates to a macro-expansion-time environment.
+The
+.code with-update-expander
+macro uses this environment to perform macro-expansion on the value of the
+.meta place
+form, to obtain the correct update expander function for the fully
+macro-expanded place.
+
+The place-access code is generated by calling the update expander
+for the expanded version of
+.codn place .
+
+.TP* "Example:"
+
+The following is an implementation of the
+.code swap
+macro, which exchanges the contents of two places.
+
+Two places are involved, and, correspondingly, the
+.code with-update-expander
+macro is used twice, to add two instances of place-update code
+to the macro's body.
+
+.cblk
+ (defmacro swap (place-0 place-1 :env env)
+ (with-gensyms (tmp)
+ (with-update-expander (getter-0 setter-0) place-0 env
+ (with-update-expander (getter-1 setter-1) place-1 env
+ ^(let ((,tmp (,getter-0)))
+ (,setter-0 (,getter-1))
+ (,setter-1 ,tmp))))))
+.cble
+
+The basic logic for swapping two places is contained in the code template:
+
+.cblk
+ ^(let ((,tmp (,getter-0)))
+ (,setter-0 (,getter-1))
+ (,setter-1 ,tmp))
+.cble
+
+The temporary variable named by the
+.code gensym
+symbol
+.code tmp
+is initialized by calling the getter function for
+.metn place-0 .
+Then the setter function of
+.meta place-0
+is called in order to store the value of
+.meta place-1
+into
+.metn place-0 .
+Finally, the setter for
+.meta place-1
+is invoked to store the previously saved temporary value into
+that place.
+
+The name for the temporary variable is provided by the
+.code with-gensyms
+macro, but establishing the variable is the caller's responsibility;
+this is seen as an explicit
+.code let
+binding in the code template.
+
+The names of the getter and setter functions are similarly provided
+by the
+.code with-update-expander
+macros. However, binding those functions is the responsibility of that
+macro. To achieve this, it adds the place-access code to the code generated by
+the
+.code ^(let ...)
+backquote template. In the following example macro-expansion, the additional
+code added around the template is seen. It takes the form of two
+.code macrolet
+binding blocks, each added by an invocation of
+.codn with-update-expander :
+
+.cblk
+ (macroexpand '(swap a b))
+
+ -->
+
+ (macrolet ((#:g0036 () 'a) ;; getter macro for a
+ (#:g0037 (val-expr) ;; setter macro for a
+ (append (list 'sys:setq) (list 'a)
+ (list val-expr))))
+ (macrolet ((#:g0038 () 'b) ;; getter macro for b
+ (#:g0039 (val-expr) ;; setter macro for b
+ (append (list 'sys:setq) (list 'b)
+ (list val-expr))))
+ (let ((#:g0035 (#:g0036))) ;; temp <- a
+ (#:g0037 (#:g0038)) ;; a <- b
+ (#:g0039 #:g0035)))) ;; b <- temp
+.cble
+
+In this expansion, for example
+.code #:g0036
+is the generated symbol which forms the value of the
+.code getter-0
+variable in the
+.code swap
+macro. The getter is a macro which simply expands to a
+.codn a :
+straightforward access to the variable a.
+Of course,
+.code #:g0035
+is nothing but the value of the
+.code tmp
+variable. Thus the swap macro's
+.cblk
+^(let ((,tmp (,getter-0))) ...)
+.cble
+has turned into
+.cblk
+^(let ((#:g0035 (#:g0036))) ...)
+.cble
+
+A full expansion, with the
+.code macrolet
+local macros expanded out:
+
+.cblk
+ (sys:expand '(swap a b))"
+
+ -->
+
+ (let ((#:g0035 a))
+ (sys:setq a b)
+ (sys:setq b #:g0035))
+.cble
+
+In other words, the original syntax
+.cblk
+(,getter-0)
+.cble
+became
+.cblk
+(#:g0036)
+.cble
+and finally just
+.codn a .
+
+Similarly,
+.cblk
+(,setter-0 (,getter-1))
+.cble
+became the
+.code macrolet
+invocations
+.cblk
+(#:g0037 (#:g0038))
+.cble
+which finally turned into:
+.codn "(sys:setq a b)" .
+
+.coNP Macro @ with-clobber-expander
+.synb
+.mets (with-clobber-expander <> ( simple-setter ) < place < env
+.mets \ << body-form )
+.syne
+.desc
+The
+.code with-clobber-expander
+macro evaluates
+.metn body-form ,
+whose result is expected to be a Lisp form. The macro adds additional code
+around this form, and the result is returned. This additional code is called
+the
+.IR "place-access code" .
+
+The
+.meta simple-setter
+argument must be a symbol. Over the evaluation of the
+.metn body-form ,
+this symbol is bound to the name of a functions which
+are provided in the place-access code.
+
+The
+.meta place
+argument is a form which evaluates to a syntactic place. The generated
+place-access code is based on this place.
+
+The
+.meta env
+argument is a form which evaluates to a macro-expansion-time environment.
+The
+.code with-clobber-expander
+macro uses this environment to perform macro-expansion on the value of the
+.meta place
+form, to obtain the correct update expander function for the fully
+macro-expanded place.
+
+The place-access code is generated by calling the update expander
+for the expanded version of
+.codn place .
+
+.TP* "Example:"
+
+The following implements a simple assignment statement, similar to
+.code set
+except that it only handles exactly two arguments:
+
+.cblk
+ (defmacro assign (place new-value :env env)
+ (with-clobber-expander (setter) place env
+ ^(,setter ,new-value)))
+.cble
+
+Note that the correct evaluation order of
+.code place
+and
+.code new-value
+is taken care of, because
+.code with-clobber-expander
+generates the code which performs all the necessary evaluations of
+.codn place .
+This evaluation occurs before the code which is generated by
+.cblk
+^(,setter ,new-value)
+.cble
+part is evaluated, and that code is what evaluates
+.codn new-value .
+
+Suppose that a macro were desired which allows assignment to be notated in a right to left
+style, as in:
+
+.cblk
+ (assign 42 a) ;; store 42 in variable a
+.cble
+
+Now, the new value must be evaluated prior to the place, if left to right
+evaluation order is to be maintained. The standard
+.code push
+macro has this property: the push value is on the left, and the place
+is on the right.
+
+Now, the code has to explicitly take care of the order, like this:
+
+.cblk
+ ;; WRONG! We can't just swap the parameters;
+ ;; place is still evaluated first, then new-value:
+
+ (defmacro assign (new-value place :env env)
+ (with-clobber-expander (setter) place env
+ ^(,setter ,new-value)))
+
+ ;; Correct: arrange for evaluation of new-value first,
+ ;; then place:
+
+ (defmacro assign (new-value place :env env)
+ (with-gensym (tmp)
+ ^(let ((,tmp ,new-value))
+ ,(with-clobber-expander (setter) place env
+ ^(,setter ,tmp)))))
+.cble
+
+.coNP Macro @ with-delete-expander
+.synb
+.mets (with-delete-expander <> ( deleter ) < place < env
+.mets \ << body-form )
+.syne
+.desc
+The
+.code with-delete-expander
+macro evaluates
+.metn body-form ,
+whose result is expected to be a Lisp form.
+The macro adds additional code
+around this code, and the resulting code is returned. This additional code is
+called the
+.IR "place-access code" .
+
+The
+.meta deleter
+argument must be a symbol. Over the evaluation of the
+.metn body-form ,
+this symbol is bound to the name of a functions which
+are provided in the place-access code.
+
+The
+.meta place
+argument is a form which evaluates to a syntactic place. The generated
+place-access code is based on this place.
+
+The
+.meta env
+argument is a form which evaluates to a macro-expansion-time environment.
+The
+.code with-delete-expander
+macro uses this environment to perform macro-expansion on the value of the
+.meta place
+form, to obtain the correct update expander function for the fully
+macro-expanded place.
+
+The place-access code is generated by calling the update expander
+for the expanded version of
+.codn place .
+
+.TP* "Example:"
+
+The following implements the
+.code del
+macro:
+
+.cblk
+ (defmacro del (place :env env)
+ (with-delete-expander (deleter) place env
+ ^(,deleter)))
+.cble
+
+.coNP Function @ call-update-expander
+.synb
+.mets (call-update-expander < getter < setter < place < env << body-form )
+.syne
+.desc
+The
+.code call-update-expander
+function provides an alternative interface for making use of an update
+expander, complementary to
+.codn with-update-expander .
+
+Arguments
+.meta getter
+and
+.meta setter
+are symbols, provided by the caller. These are passed to the update
+expander function, and are used for naming local functions in the
+generated code which the update expander adds to
+.metn body-form .
+
+The
+.meta place
+argument is a place which has not been subject to macro-expansion.
+The
+.code call-update-expander
+function takes on the responsibility for macro-expanding the place.
+
+The
+.meta env
+parameter is the macro-expansion environment object required to
+correctly expand
+.code place
+in its original environment.
+
+The
+.meta body-form
+argument represents the source code of a place update operation.
+This code makes references to the local functions whose names
+are given by
+.meta getter
+and
+.metn setter .
+Those arguments allow the update expander to write these functions
+with the matching names expected by
+.metn body-form .
+
+The return value is an object representing source code which incorporates
+the
+.metn body-form ,
+augmenting it with additional code which evaluates
+.code place
+to determine its location, and provides place accessor local functions
+expected by the
+.metn body-form .
+
+.TP* "Example:"
+
+The following shows how to implement a
+.code with-update-expander
+macro using
+.codn call-update-expander :
+
+.cblk
+ (defmacro with-update-expander ((getter setter)
+ unex-place env body)
+ ^(with-gensyms (,getter ,setter)
+ (call-update-expander ,getter ,setter
+ ,unex-place ,env ,body)))
+.cble
+
+Essentially, all that
+.code with-update-expander
+does is to choose the names for the local functions, and bind them
+to the local variable names it is given as arguments. Then it
+calls
+.codn call-update-expander .
+
+.TP* "Example:"
+
+Implement the swap macro using
+.codn call-update-expander :
+
+.cblk
+ (defmacro swap (place-0 place-1 :env env)
+ (with-gensyms (tmp getter-0 setter-0 getter-1 setter-1)
+ (call-update-expander getter-0 setter-0 place-0 env
+ (call-update-expander getter-1 setter-1 place-1 env
+ ^(let ((,tmp (,getter-0)))
+ (,setter-0 (,getter-1))
+ (,setter-1 ,tmp))))))
+.cble
+
+.coNP Function @ call-clobber-expander
+.synb
+.mets (call-clobber-expander < simple-setter < place < env << body-form )
+.syne
+.desc
+The
+.code call-clobber-expander
+function provides an alternative interface for making use of a clobber
+expander, complementary to
+.codn with-clobber-expander .
+
+Argument
+.meta simple-setter
+is a symbol, provided by the caller. It is passed to the clobber
+expander function, and is used for naming a local function in the
+generated code which the update expander adds to
+.metn body-form .
+
+The
+.meta place
+argument is a place which has not been subject to macro-expansion.
+The
+.code call-clobber-expander
+function takes on the responsibility for macro-expanding the place.
+
+The
+.meta env
+parameter is the macro-expansion environment object required to
+correctly expand
+.code place
+in its original environment.
+
+The
+.metn body-form
+argument represents the source code of a place update operation.
+This code makes references to the local function whose name
+is given by
+.metn simple-setter .
+That argument allows the update expander to write this function
+with the matching name expected by
+.metn body-form .
+
+The return value is an object representing source code which incorporates
+the
+.metn body-form ,
+augmenting it with additional code which evaluates
+.code place
+to determine its location, and provides the clobber local function
+to the
+.metn body-form .
+
+.coNP Function @ call-delete-expander
+.synb
+.mets (call-delete-expander < deleter < place < env << body-form )
+.syne
+.desc
+The
+.code call-delete-expander
+function provides an alternative interface for making use of a delete
+expander, complementary to
+.codn with-delete-expander .
+
+Argument
+.meta deleter
+is a symbol, provided by the caller. It is passed to the delete
+expander function, and is used for naming a local function in the
+generated code which the update expander adds to
+.metn body-form .
+
+The
+.meta place
+argument is a place which has not been subject to macro-expansion.
+The
+.code call-delete-expander
+function takes on the responsibility for macro-expanding the place.
+
+The
+.meta env
+parameter is the macro-expansion environment object required to
+correctly expand
+.code place
+in its original environment.
+
+The
+.meta body-form
+argument represents the source code of a place delete operation.
+This code makes references to the local function whose name
+is given by
+.metn deleter .
+That argument allows the update expander to write this function
+with the matching name expected by
+.metn body-form .
+
+The return value is an object representing source code which incorporates
+the
+.metn body-form ,
+augmenting it with additional code which evaluates
+.code place
+to determine its location, and provides the delete local function
+to the
+.metn body-form .
+
+.coNP Macro @ define-modify-macro
+.synb
+.mets (define-modify-macro < name < parameter-list << function-name )
+.syne
+.desc
+The
+.code define-modify-macro
+macro provides a simplified way to write certain kinds of place update
+macros. Specifically, it provides a way to write place update macros
+which modify a place by retrieving the previous value, pass it through
+a function (perhaps together with some additional arguments), and then store
+the resulting value back into the place and return it.
+
+The
+.meta name
+parameter specifies the name for the place update macro to be written.
+
+The
+.meta function-name
+parameter must specify a symbol: the name of the update function.
+
+The update macro and update function both take at least one parameter:
+the place to be updated, and its value, respectively.
+
+The
+.meta parameter-list
+specifies the additional parameters for update function, which will also
+become additional parameters of the macro. Because it is a
+function parameter list, it cannot use the special destructuring features of
+macro parameter lists, or the
+.code :env
+or
+.code :whole
+special parameters. It can use optional parameters. Of course, it may be empty.
+
+The
+.code define-modify-macro
+macro writes a macro called
+.metn name .
+The leftmost parameter of this macro is a place, followed by the additional arguments
+specified by
+.metn parameter-list .
+The macro will arrange for the evaluation of the place argument to determine
+the place location. It will then retrieve and save the prior value of the
+place, and evaluate the remaining arguments. The prior value of the
+place, and the values of the additional arguments, are all passed to
+.meta function
+and the resulting value is then stored back into the location previously
+determined for
+.metn place .
+
+.TP* "Example:"
+
+Some standard place update macros are implementable using
+.codn define-modify-macro ,
+such as
+.codn inc .
+
+The
+.code inc
+macro reads the old value of the place, then passes it through the
+.code +
+(plus) function, along with an extra argument: the delta value, which
+defaults to one. The
+.code inc
+macro could be written using
+.code define-modify-macro
+as follows:
+
+.cblk
+ (define-modify-macro inc (: (delta 1)) +)
+.cble
+
+Note that the argument list
+.code (: (delta 1))
+doesn't specify the place, because the place is the implicit leftmost
+argument of the macro which isn't given a name. With the above definition
+in place, when
+.code (inc (car a))
+is invoked, then
+.code (car a)
+is first reduced to a location, and that location's value is retrieved and
+saved. Then the
+.code delta
+parameter s evaluated to its value, which has defaulted to 1, since
+the argument was omitted.
+Then these two values are passed to the
+.code +
+function, and so 1 is added to the value previously retrieved from
+.codn (car a) .
+The resulting sum is then stored back
+.code (car a)
+without, of course, evaluating
+.code (car a)
+again.
+
+.coNP Macro @ defplace
+.synb
+.mets (defplace < place-destructuring-args < body-sym
+.mets \ \ \ \ \ \ \ \ \ >> ( getter-sym < setter-sym << update-body )
+.mets \ \ \ \ \ \ \ \ \ >> [( ssetter-sym << clobber-body )
+.mets \ \ \ \ \ \ \ \ \ \ >> [( deleter-sym << delete-body )]])
+.syne
+.desc
+The
+.code defplace
+macro is used to introduce a new kind of syntactic place.
+It writes the update expander, and optionally clobber and delete
+expander functions, from a simpler, more compact specification,
+and automatically registers the resulting functions. The compact specification
+of a
+.code defplace
+call contains only code fragments for the expander functions.
+
+The name and syntax of the place is determined by the
+.meta place-destructuring-args
+argument, which is macro-style parameter list whose structure
+mimics that of the the place. In particular, its leftmost symbol
+gives the name under which the place is registered.
+The
+.code defplace
+macro provides automatic destructuring of the syntactic place,
+so that the expander code fragments can refer to the components
+of a place by name.
+
+The
+.meta body-sym
+parameter must be be a symbol. This symbol will capture the
+.meta body-forms
+parameter which is passed to the update expander, clobber
+expander or delete expander. The code fragments then have
+access to the the body forms via this name.
+
+The
+.metn getter-sym ,
+.metn setter-sym ,
+and
+.metn update-body
+parenthesized triplet specify the update expander fragment.
+The
+.code defplace
+macro will bind
+.meta getter-sym
+and
+.meta setter-sym
+to symbols. The
+.meta update-body
+must then specify a template of code which evaluates the syntactic place to
+determine its storage location, and provides a pair of local functions, using
+these two symbols as their name. The template must also insert the
+.meta body-sym
+forms into the scope of these local functions, and the place determining code.
+
+The
+.meta setter-sym
+and
+.meta clobber-body
+arguments similarly specify an optional clobber expander fragment,
+as a single optional argument. If specified, the
+.meta clobber-body
+must generate a local function named using
+.meta setter-sym
+wrapped around
+.meta body-sym
+forms.
+
+The
+.meta deleter-sym
+and
+.meta deleter-body
+likewise specify a delete expander fragment. If this is omitted,
+then the place shall not support deletion.
+
+.TP* "Example:"
+
+Implementation of the place denoting the
+.code car
+field of
+.code cons
+cells:
+
+.cblk
+ (defplace (car cell) body
+
+ ;; the update expander fragment
+ (getter setter
+ (with-gensyms (cell-sym) ;; temporary symbol for cell
+ ^(let ((,cell-sym ,cell)) ;; evaluate place to cell
+ ;; getter and setter access cell via temp var
+ (macrolet ((,getter ()
+ ^(car ,',cell-sym))
+ (,setter (val)
+ ^(sys:rplaca ,',cell-sym ,val)))
+
+ ;; insert body form from place update macro
+ ,body))))
+
+ ;; clobber expander fragment: simpler: no need
+ ;; to evaluate cell to temporary variable.
+ (ssetter
+ ^(macrolet ((,ssetter (val)
+ ^(sys:rplaca ,',cell ,val)))
+ ,body))
+
+ ;; deleter: delegate to pop semantics:
+ ;; (del (car a)) == (pop a).
+ (deleter
+ ^(macrolet ((,deleter () ^(pop ,',cell)))
+ ,body)))
+.cble
+
+.coNP Macro @ rlet
+.synb
+.mets (rlet >> ({( sym << init-form )}*) << body-form *)
+.syne
+.desc
+The macro
+.code rlet
+is similar to the
+.code let
+operator. It establishes bindings for one or more
+.metn sym -s,
+which are initialized using the values of
+.metn init-form -s.
+
+Note that the simplified syntax for a variable which initializes to
+.code nil
+by default is not supported by
+.codn rlet ;
+that is to say, the syntax
+.meta sym
+cannot be used in place of the
+.meti >> ( sym << init-form )
+syntax when
+.meta sym
+is to be initialized to
+.codn nil .
+
+The
+.code rlet
+macro differs from
+.code let
+in that
+.code rlet
+assumes that those
+.metn sym -s
+which have constant
+.metn init-form -s
+(according to the
+.code constantp
+function) may be safely implemented as a symbol macro rather than a lexical
+variable.
+
+Therefore
+.code rlet
+is suitable in situations in which simpler code is desired from the output
+of certain kinds of machine-generated code, which binds local symbols:
+code with fewer temporary variables.
+
+On the other hand,
+.code rlet
+is not suitable in situations when true variables are required, which
+are assignable, and provide temporary storage.
+
+.TP* "Example:"
+
+.cblk
+ ;; WRONG! Exchange two variables, a and b:
+ (rlet ((temp a))
+ (set a b)
+ (set b temp))
+
+ ;; Demonstration of constant-propagation
+ (let ((a 42))
+ (rlet ((x 1)
+ (y a))
+ (+ x y))) --> 43
+
+ (sys:expand
+ '(let ((a 42))
+ (rlet ((x 1)
+ (y a))
+ (+ x y)))) --> (let ((a 42))
+ (let ((y a))
+ (+ 1 y)))
+.cble
+
+The last example shows that the
+.code x
+variable has disappeared in the expansion. The
+.code rlet
+macro turned it into into a
+.code symacrolet
+denoting the constant 1, which then propagated to the use site,
+turning the expression
+.code (+ x y)
+into
+.codn (+ 1 y) .
+
+.coNP Macro @ with-gensyms
+.synb
+.mets (with-gensyms <> ( sym *) << body-form *)
+.syne
+.desc
+The
+.code with-gensyms
+evaluates the
+.metn body-form -s
+in an environment in which each variable name symbol
+.meta sym
+is bound to a new uninterned symbol ("gensym").
+
+.TP* "Example:"
+
+The code:
+
+.cblk
+ (let ((x (gensym))
+ (y (gensym))
+ (z (gensym)))
+ ^(,x ,y ,z))
+.cble
+
+may be expressed more conveniently using the
+.code with-gensyms
+shorthand:
+
+.cblk
+ (with-gensyms (x y z)
+ ^(,x ,y ,z))
+.cble
+
.SS* Debugging Functions
.coNP Functions source-loc and source-loc-str
.synb