Common Lisp the Language, 2nd Edition
Sometimes a particular data representation is imposed by external requirements, and yet it is desirable to document the data format as a defstruct-style structure. For example, consider expressions built up from numbers, symbols, and binary operations such as + and *. An operation might be represented as it is in Lisp, as a list of the operator and the two operands. This fact can be expressed succinctly with defstruct in this manner:
(defstruct (binop (:type list)) (operator '? :type symbol) operand-1 operand-2)
This will define a constructor function make-binop and three selector functions, namely binop-operator, binop-operand-1, and binop-operand-2. (It will not, however, define a predicate binop-p, for reasons explained below.)
The effect of make-binop is simply to construct a list of length 3:
(make-binop :operator '+ :operand-1 'x :operand-2 5) => (+ x 5) (make-binop :operand-2 4 :operator '*) => (* nil 4)
It is just like the function list except that it takes keyword arguments and performs slot defaulting appropriate to the binop conceptual data type. Similarly, the selector functions binop-operator, binop-operand-1, and binop-operand-2 are essentially equivalent to car, cadr, and caddr, respectively. (They might not be completely equivalent because, for example, an implementation would be justified in adding error-checking code to ensure that the argument to each selector function is a length-3 list.)
We speak of binop as being a ``conceptual'' data type because binop is not made a part of the Common Lisp type system. The predicate typep will not recognize binop as a type specifier, and type-of will return list when given a binop structure. Indeed, there is no way to distinguish a data structure constructed by make-binop from any other list that happens to have the correct structure.
There is not even any way to recover the structure name binop from a structure created by make-binop. This can be done, however, if the structure is ``named.''