A stobj field mapping stobj names to stobjs
See stobj for basic background on stobjs, and see defstobj for detailed documentation on the syntax and semantics of stobjs,
including fields specified with
For examples of
A stobj-table field may be viewed as an association list mapping stobj
names to corresponding stobjs, so that each stobj name maps to a stobj that
satisfies the recognizer for that stobj name. This requirement is not
enforced in the logic (i.e., in recognizer functions). But it is essentially
maintained in raw Lisp as an invariant, except that for efficiency, a
stobj-table field is implemented as a hash table (again, mapping stobj names
to corresponding stobjs). Below we give more details and explain how
The general forms for stobj-table fields of stobjs are as shown below.
(defstobj NAME (TBL1 :type (stobj-table)) ; stobj field of default size ... (TBL2 :type (stobj-table SIZE)) ; stobj field of desired size SIZE ...)
That is, a stobj-table field is a field whose type is of the form
As noted above, a stobj-table is conceptually an alist that associates
stobj names with corresponding stobjs. We say ``conceptually'' because in
fact, the recognizer for any stobj-table field is
A common case is that a stobj-table field is the unique field of its parent stobj. In that case we may think of the entire stobj as a stobj-table, and that is actually consistent with the implementation: in raw Lisp, if a stobj-table field is the sole field of its parent stobj, then that stobj-table field is actually the entire stobj; there is not the usual indirection in raw Lisp, where a stobj is a vector of fields and the field is accessed as an entry in that vector. (Logically, however, a stobj is always a list of its fields, even if there is only one field.)
Reads and writes of a stobj-table field are much like reads and writes when
the field is an array of stobjs. In both cases, one uses stobj-let to
access and possibly update the desired individual stobj or stobjs. The syntax
thus looks as follows, where
(stobj-let (... ; bindings (ST (TBL-GET 'ST PARENT (CREATE-ST))) ... ) (...) ; producer variables (...) ; producer ... ; consumer )
The
Remark. This remark on the implementation may be skipped, but it
may provide some intuition. When a stobj name is not bound in the underlying
hash table, then the default value (in the example above,
The remainder of this topic explains the use of stobj-tables by following
the example in community-book
(defstobj stobj-table (tbl :type (stobj-table)))
This specifies a stobj named
The remaining forms in the book are local to the book, intended to illustrate with simple examples how stobj-tables may be used. The first one just introduces a rather trivial stobj.
(defstobj st1 fld1)
The next form puts this stobj into the stobj-table after updating its field with a specified value.
(defun update-st1-in-tbl (val stobj-table) (declare (xargs :stobjs stobj-table)) (stobj-let ((st1 (tbl-get 'st1 stobj-table (create-st1)))) ; bindings (st1) ; producer variable (update-fld1 val st1) ; producer stobj-table ; consumer ))
The form after that accesses the value of
(defun read-st1-in-tbl1 (stobj-table) (declare (xargs :stobjs stobj-table)) (stobj-let ((st1 (tbl-get 'st1 stobj-table (create-st1)))) ; bindings (val) ; producer variable (fld1 st1) ; producer val ; consumer ))
Next is a check that when a specific value is written for the key
(assert-event (let ((stobj-table (update-st1-in-tbl 3 stobj-table))) (mv (equal (read-st1-in-tbl1 stobj-table) 3) stobj-table)) :stobjs-out '(nil stobj-table))
The computation above is a special case of a standard ``read-over-write'' property, that after a write, we read the value that was just written. This is proved automatically.
(defthm read-over-write-st1-in-tbl (implies (stobj-tablep stobj-table) (let ((stobj-table (update-st1-in-tbl val stobj-table))) (equal (read-st1-in-tbl1 stobj-table) val))))
The other standard ``read-over-write'' property is that the write for a
given key doesn't affect the value read for a different key. The following
events, admitted automatically, illustrate that property: here, writing a
value for the key
(defstobj st2 fld2) (defun read-st2-in-tbl (stobj-table) (declare (xargs :stobjs stobj-table)) (stobj-let ((st2 (tbl-get 'st2 stobj-table (create-st2)))) ; bindings (val) ; producer variable (fld2 st2) ; producer val ; consumer )) (defthm read-over-write-st2-in-tbl (implies (stobj-tablep stobj-table) (let ((stobj-table-2 (update-st1-in-tbl val stobj-table))) (equal (read-st2-in-tbl stobj-table-2) (read-st2-in-tbl stobj-table)))))
Note that all keys in a stobj-table are names of stobjs. Consider what
happens when we evaluate the events in the book above, including the local
events, and then undo the defstobj event admitting
ACL2 !>(ld "std/stobjs/stobj-table.lisp" :dir :system) [[.. output omitted ..]] ACL2 !>(tbl-count stobj-table) 1 ACL2 !>:ubt st1 d 1:x(DEFSTOBJ STOBJ-TABLE (TBL :TYPE #)) ACL2 !>(tbl-count stobj-table) 0 ACL2 !>
Finally, we remark that there is nothing special about stobj-table fields with respect to stobj fields of abstract stobjs.