Creating a Space InstanceTopEnhancing Your Development EnvironmentDeleting InstancesGoTo Top

Deleting Instances

GBBopen retains created unit and space instances until they are explicitly deleted. This behavior is important to blackboard applications, where shared information in the form of unit instances remains available until a decision is made to remove them.

In this exercise, we explore some implications of deleting unit and space instances.


This exercise shows you how to:


Prerequisites

If you ended the Common Lisp session used in the last exercise, begin a new session and evaluate the following forms:

  cl-user> (load "<install-dir>/initiate.lisp")
     ...
  cl-user> :gbbopen-user
     ...
  gbbopen-user> (define-unit-class location ()
                   (x y))
  #<standard-unit-class location>
  gbbopen-user> (defparameter ui (make-instance 'location :x 50 :y 60))
  ui
  gbbopen-user> (defparameter si (make-space-instance '(known-world)))
  si
  gbbopen-user> (add-instance-to-space-instance 
                   (make-instance 'location :x 80 :y 90)
                   si)
  #<location 2>
  gbbopen-user>

ASDF, clbuild, and Quicklisp users
Remember to (asdf:operate 'asdf:load-op 'gbbbopen) or (require :gbbopen) in place of loading <install-dir>/initiate.lisp.

Step 1: Create a few more unit instances

Just to review, we created two location unit instances. The unit instance named 1 is no longer on the known-world space instance, but it is still assigned to the global variable ui:

  gbbopen-user> ui
  #<location 1> 
  gbbopen-user>
The location unit instance named 2 is the only location unit instance on the known-world space instance:
  gbbopen-user> (find-instances 'location '(known-world) :all)
  (#<location 2>)
  gbbopen-user>

Now, let's create five more location unit instances. Enter:

  gbbopen-user> (dotimes (i 5) (make-instance 'location))
  nil
  gbbopen-user>
Note that we did not specify x and y slot values for these new unit instances. We will explore the implications of this shortly.

Step 2: Apply a function to all instances of a unit class

We did not assign the new location unit instances to global variables or add them to the known-world space instance. Can we still reference them? If we know the names of the new unit instances, we can use the find-instance-by-name function that we learned earlier. For example:

  gbbopen-user> (find-instance-by-name 5 'location)
  #<location 5>
  gbbopen-user>

It is often useful to perform some action on all instances of a unit class. GBBopen provides a mapping function, or “iterator,” that repeatedly calls a function with each instance of a unit class as the argument to the function. For example:

  gbbopen-user> (map-instances-of-class #'print 'location)

  #<location 6> 
  #<location 3> 
  #<location 7> 
  #<location 1> 
  #<location 2> 
  #<location 4> 
  #<location 5> 
  nil
  gbbopen-user>
displays each of our location unit instances. Note that the exact order that location unit instances are supplied to the print function may differ from the above example in your Common Lisp implementation.

Currently, there is only one location unit instance on the known-world space instance:

  gbbopen-user> (find-instances 'location '(known-world) :all)
  (#<location 2>)
  gbbopen-user>

Let's use map-instances-of-class to add all the location unit instances to the known-world:

  gbbopen-user> (map-instances-of-class 
                  #'(lambda (instance) 
                       (add-instance-to-space-instance instance si))
                 'location)
  Warning: In add-instance-to-space-instance: #<location 2> is already on 
           space instance #<standard-space-instance (known-world)>.
  nil
  gbbopen-user>
GBBopen warns us that the location 2 unit instance is already on the known-world and adds all the other location instances. We can verify this by using find-instances:
  gbbopen-user> (find-instances 'location '(known-world) :all)
  (#<location 7> #<location 6> #<location 5> #<location 4> #<location 3>
   #<location 2> #<location 1>)
  gbbopen-user>

For those who prefer a more iterative programming style, GBBopen provides a dolist-style macro, do-instances-of-class, as an alternative to map-instances-of-class. So, to add all the location unit instances to the known-world, we could have chosen to use the form:

  (do-instances-of-class (instance 'location)
     (add-instance-to-space-instance instance si))
instead of the map-instances-of-class version. As with things stylistic, the choice is yours.

Step 3: Delete a unit instance

Let's delete the first location unit instance that we created:

  gbbopen-user> (delete-instance ui)
  #<deleted-unit-instance location 1>
  gbbopen-user>
Note that the displayed representation indicates that the unit instance has been deleted.

We can no longer find the deleted unit instance by its name:

  gbbopen-user> (find-instance-by-name 1 'location)
  nil
  gbbopen-user>
and it is no longer included in a map-instances-of-class iteration:
  gbbopen-user> (map-instances-of-class #'print 'location)

  #<location 6> 
  #<location 3> 
  #<location 7> 
  #<location 2> 
  #<location 4> 
  #<location 5> 
  nil
  gbbopen-user>
and it is no longer on the known-world space instance:
  gbbopen-user> (find-instances 'location '(known-world) :all)
  (#<location 7> #<location 6> #<location 5> #<location 4> #<location 3>
   #<location 2>)
  gbbopen-user>

However, the deleted location unit instance is still assigned to the ui global variable:

  gbbopen-user> ui
  #<deleted-unit-class location 1>
  gbbopen-user>
and that can lead to problems. Let's try to place the deleted location unit instance on the known-world space instance:
  gbbopen-user> (add-instance-to-space-instance ui si)
  Error: No methods applicable for generic function
         #<standard-generic-function add-instance-to-space-instance> with args
         (#<deleted-unit-instance location 1> #<standard-space-instance (known-world)>)
         of classes (deleted-unit-instance standard-space-instance)
  gbbopen-user>> :a
  gbbopen-user>

Most of GBBopen's operations signal an error if they are given a deleted unit instance. This is because delete-instance changes the class of the deleted unit instance to deleted-unit-instance which, despite its name, is not a standard-unit-instance. We cannot use describe-instance to verify this, because the deleted unit instance is no longer a unit instance, but we can use Common Lisp's describe function:

  gbbopen-user> (describe ui)
  #<deleted-unit-instance location 1> is an instance of
    #<standard-class deleted-unit-instance>:
  The following slots have :instance allocation:
    instance-name   1
    original-class  #<standard-unit-class location>
  gbbopen-user>
Note that the slots that we defined for location unit instances are not present in the deleted-unit-instance object. A deleted unit instance has only two slots, instance-name and original-class, which are set when the class of the deleted unit instance is changed to deleted-unit-instance.

Several GBBopen operations on a deleted unit instance do not signal errors. In particular:

  gbbopen-user> (instance-name-of ui)
  1
  gbbopen-user>
The value returned by instance-name-of, along with that by original-class-of, can be used to identify the unit instance that, when deleted, became the deleted-unit-instance.

Typically, blackboard applications obtain unit instances from the blackboard repository (or as we will see in the Using a Control Shell exercise, as an event argument) rather than maintaining references to them in variables. This limits the possibility of retaining a deleted unit instance and performing GBBopen operations on it. The deletion status of a unit instance can be determined using the instance-deleted-p predicate:

  gbbopen-user> (instance-deleted-p ui)
  t
  gbbopen-user>

Step 4: Create a simple space-instance hierarchy

In the last exercise, we noted that space instances can be organized in hierarchical structures. To illustrate this, let's create a few more space instances:

  gbbopen-user> (make-space-instance '(known-world my-town))
  #<standard-space-instance (known-world my-town)>
  gbbopen-user> (make-space-instance '(known-world my-town east-side))
  #<standard-space-instance (known-world my-town east-side)>
  gbbopen-user> (make-space-instance '(known-world my-town west-side))
  #<standard-space-instance (known-world my-town west-side)>
  gbbopen-user>

Recall that the space-instance-path argument to make-space-instance is the complete list of space-instance names, starting with the name of the most distant indirect parent. So, the my-town space instance has the known-world as it's parent and the east-side and west-side as children. We can use describe-blackboard-repository to see this organization:

  gbbopen-user> (describe-blackboard-repository)

  Space Instance                Contents
  --------------                --------
  known-world                   6 instances (6 location)
     my-town                    Empty
        east-side               Empty
        west-side               Empty

  Unit Class                    Instances
  ----------                    ---------
  location                              6
  standard-space-instance               4
                                ---------
                                       10 instances
  gbbopen-user>

Observe from the above description that child space instances that we created are not placed on their parent. Unlike the directories in a file system in the analogy that we presented in the last exercise, in GBBopen a space-instance hierarchy is orthogonal to containment. In fact, space instances are actually unit instances (of the class standard-space-instance, by default) and a space instance can be placed on other space instances—or even on itself. Typical blackboard applications do not involve using space instances as unit instances, but this property of space instances is very powerful when it is needed.

The functions parent-of and children-of are provided to traverse the space-instance hierarchy. For example:

  gbbopen-user> (children-of 
                  (find-space-instance-by-path '(known-world my-town)))
  (#<standard-space-instance (known-world my-town west-side)>
   #<standard-space-instance (known-world my-town east-side)>)
  gbbopen-user>

Step 5: Add a unit instance to multiple space instances

Now, let's add location 2 to the my-town and east-side space instances:

  gbbopen-user> (let ((location-2 (find-instance-by-name 2 'location)))
                 (add-instance-to-space-instance location-2 '(known-world my-town))
                 (add-instance-to-space-instance location-2 '(known-world my-town east-side)))
  #<location 2>
  gbbopen-user>
As we expect, location 2 is now on three space instances:
  gbbopen-user> (describe-instance (find-instance-by-name 2 'location))
  Location #<location 2>
    Instance name: 2
    Space instances: ((known-world my-town east-side)
                      (known-world my-town)
                      (known-world))
    Dimensional values: None
    Non-link slots:
      x:  80
      y:  90
    Link slots: None
  gbbopen-user>
and the description of the blackboard repository is:
  gbbopen-user> (describe-blackboard-repository)

  Space Instance                Contents
  --------------                --------
  known-world                   6 instances (6 location)
     my-town                    1 instance (1 location)
        east-side               1 instance (1 location)
        west-side               Empty

  Unit Class                    Instances
  ----------                    ---------
  location                              6
  standard-space-instance               4
                                ---------
                                       10 instances
  gbbopen-user>

Placing a unit instance on multiple space instances is useful when each space instance represents a different view of unit instances. In this case, location 2 is in the known-world, in my-town, and on the east-side:

  gbbopen-user> (find-instances 'location '(known-world my-town) :all)
  (#<location 2>)
  gbbopen-user> (find-instances 'location '(known-world my-town east-side) :all)
  (#<location 2>)
  gbbopen-user>

Step 6: Delete a space instance

Now let's delete some of what we just created. Let's delete the my-town space instance:

  gbbopen-user> (delete-space-instance '(known-world my-town))
  #<deleted-unit-instance standard-space-instance (known-world my-town)>
  gbbopen-user>
and describe the blackboard repository:
  gbbopen-user> (describe-blackboard-repository)

  Space Instance                Contents
  --------------                --------
  known-world                   6 instances (6 location)

  Unit Class                    Instances
  ----------                    ---------
  location                              6
  standard-space-instance               1
                                ---------
                                        7 instances
  gbbopen-user>
Notice that GBBopen has also deleted all the child space instances of my-town: both east-side and west-side were also deleted. So, just as with our file-system directory analogy, space-instance deletion is recursive over children and should be performed with caution.

Also note that deleting space instances did not delete any of the unit instances that were stored on them. If we want to delete a space instance and all the unit instances that are stored on it, we must explicitly delete the unit instances before deleting the space instance. For example, we could do:

  gbbopen-user> (map-instances-on-space-instances #'delete-instance 
                  'location '(known-world my-town))
  nil
  gbbopen-user>
to delete the unit instances in my-town. However, we must keep in mind that a unit instance can reside on multiple space instances; so deleting a unit instance that is on other space instances might not be the desired action.

As is the case with map-instances-of-class, GBBopen provides a do-instances-on-space-instances macro as an alternative to map-instances-on-space-instances. So we could have chosen to use the form:

  (do-instances-on-space-instances (instance 'location '(known-world my-town))
    (delete-instance instance))
to explicitly delete the location unit instances in my-town.

Step 7: Delete the Blackboard Repository

If we really want to get rid of all our unit and space instances, we can use the delete-blackboard-repository function:

  gbbopen-user> (delete-blackboard-repository)
  t
  gbbopen-user> (describe-blackboard-repository)
  There are no space instances in the blackboard repository.
  gbbopen-user> (map-instances-of-class #'print location)
  nil
  gbbopen-user>

Calling delete-blackboard-repository has deleted every space and unit instance, but it has not eliminated our location unit class definition. Let's create a location unit instance once again:

  gbbopen-user> (make-instance 'location)
  #<location 1>
  gbbopen-user>
Note that deleting the blackboard repository also reset the counter for location instance names, so the created unit instance is again named 1.


The GBBopen Project


Creating a Space InstanceTopEnhancing Your Development EnvironmentDeleting InstancesGoTo Top