defining objects in terms of identity

concepts In little b, biological entities and mathematical expressions are described using data structures called concepts. DEFCON is a macro which defines a concept class, which specifies the storage slots and behavior of data structures called concept objects, or the "instances of the class". A special syntax using the square brackets, [], is used to create instances.

In the example below, we define a class named X, with a single field, A. A field is a name used for retrieving a value from an object; it might name a storage location or a function. There are 3 kinds of fields: identity fields, property fields and method fields. Identity and property fields are storage locations; method fields are implemented as functions. In this example, A has been defined as an identity field.

defining and creating concepts
B-USER 1> (defcon x () (a)) (1) - define a concept class
{x :# #<concept-class x 2067F8FC>}

B-USER 2> [x 1] (2) - create a new instance
(new) [x 1]
[x 1]

B-USER 3> [x 1] (3) - find the existing instance
[x 1]

B-USER 4> {[x 1] = [x 1]} (4) - test for equality
[x 1] ←this indicates test succeeded

B-USER 5> {[x 1] = [x 2]} (5) - compare dissimilar objects
(new) [x 2]
NIL ←this indicates test failed

You may think of the class, {x :# #<concept-class x 2067F8FC>}, as simply a data structure which stores information which governs the behavior of its instances, which are also data structures. I'll refer to the data structure which holds the class information as "the class" or "the class object", and the instance data structures as "instances" or "objects", and use "concept" to refer either or both when the context makes this obvious.

identity Identity fields have a special role - they govern when and how concept objects are constructed. The square bracketed expressions you see on lines 2-5 (e.g., [x 1]) are examples of object-constructor syntax. During evaluation of [x 1], little b decides whether to create a new instance of X, and assign 1 to field A, or return an existing instance of X. Little b's "identity semantics" are very simple: if an instance of X already exists with field A=1, then it is retrieved and returned, otherwise, a new one is created and returned.

The identity of an object is given by its class and the value of each identity field. Identity fields are used to ensure that text which is meant to refer to a unique idea (e.g., a particular biological entity or relationship) always points to the same computational entity. Thus, every time [x 1] is evaluated, the same object is returned.

definition The DEFCON form is a macro which defines a concept. To be precise, it creates an object called a CONCEPT-CLASS, which governs how instances of that class are generated.1 After evaluating the DEFCON form (at (1) in the defining and creating concepts example), you'll see an odd looking expression printed out - something like {x :# #<concept-class x 2067F8FC>} - this indicates that a CONCEPT-CLASS named x has been defined. We'll cover the true meaning of this print out in a later section (see naming). For right now you can interpret it as "evaluating the symbol X is a way of accessing a CONCEPT-CLASS object with class-name=x". The third argument to DEFCON, "(A)", tells the macro that X has a single identity field named A.

creation The square bracket syntax (e.g., [x 1]) is used for creating and initializing objects, and is called the object constructor. Object constructors have function-style evaluation. Each of the arguments is evaluated starting from the left , and the resulting values are passed as parameters to a procedure responsible for finding or creating an instance of that class.

On the third line (2), we create an instance of X, providing a value of 1 for the A slot, using the object constructor: [x 1]. Take a look at the next line. You'll see that little b prints out (new) [x 1], then [x 1] on the line following. The "(new) [x 1]" text is little b's tracing system reporting that a fresh instance of X has been created. The second [x 1] is the Lisp READ-EVAL-PRINT loop dutifully printing the value resulting from evaluation.

When we read and evaluate "[x 1]" again (3), the tracing system doesn't print anything. Little b did not create a new instance of X. Instead, it returned the existing [x 1] object. You can test that the two objects are the same by evaluating {[x 1] = [x 1]} (line 4), or (eq [x 1] [x 1]), if you prefer Lisp prefix syntax. Note that the equality test operator, = returns the object being tested if the test succeeds - otherwise, NIL is returned.

This is really all there is to identity. As a rule of thumb, if the printed representation of two concepts instances (objects) is the same, they have the same identity, and are the same physical object in the computer's memory.

printing Note that the printed form of a concept instance looks the same as the text which was evaluated to generate it. This is one of the principles of little b, called "print eval identical", which means that the printed form of an object should be text which if evaluated should produce the identical object. Currently, "print eval identical" is used for concept objects only. Lisp uses what might be called "print read similar" semantics: reading the printed representation (but not evaluating it), should produce a similar (but not the same) object. For instance, (LIST 1 2) produces a list of two numbers, which prints (1 2). Reading "(1 2)" would produce a similar list - similar but not identical since the CONSes would be different. Evaluating (1 2) would result in error, of course, since 1 does not (and cannot) name a function or operator. It is possible to consider "print eval similar" semantics for lists, in which case this list might print as '(1 2) or (QUOTE (1 2)) or `(1 2) or (LIST 1 2), etc, all different ways of producing a list containing 1 and 2. However, this is not implemented in little b at this time: lists and symbols print with read-similar semantics.

fields We call A an identity field of X, because it determines whether two instances of the class X will be identical. The two other kinds of fields, property and method fields, do not have this behavior, as we'll see later. Notice that when "{[x 1] = [x 2]}" is evaluated (5), a new instance of X is implicitly created. As a result, this object is different from the instance created using [x 1], as shown on line 5 where the = test returns the special symbol NIL, a Lisp convention for FALSE.

The value of a field is retrieved using the dot. In our example, "[x 1].a" will return the value 1. We'll return to the dot in more detail later. First, let's take a look at how property fields are defined.

1For readers who are familiar with CLOS, the CONCEPT-CLASS is a metaclass which derives from STANDARD-CLASS; CONCEPT is a class object whose metaclass is CONCEPT-CLASS; all concept classes are subclasses of CONCEPT and therefore must also have the metaclass CONCEPT-CLASS.

properties and methods