λ

DRef Manual

Table of Contents

[in package DREF]

λ

1 Introduction

What if definitions were first-class objects?

Some defining forms do not create first-class objects. For example, defun creates function objects, but defvar does not create variable objects as no such thing exists. The main purpose of this library is to fill this gap with the introduction of xref objects:

(xref '*my-var* 'variable)
==> #<XREF *MY-VAR* VARIABLE>

xrefs just package up a name (*my-var*) and a locative (variable). They need not denote existing definitions until we actually want to use them:

(docstring (xref '*my-var* 'variable))
.. debugger invoked on LOCATE-ERROR:
..   Could not locate *MY-VAR* VARIABLE.
(defvar *my-var* nil
  "This is my var.")

(docstring (xref '*my-var* 'variable))
=> "This is my var."

Behind the scenes, the docstring function locates the definition corresponding to its xref argument, turning it into a dref:

(locate (xref '*my-var* 'variable))
==> #<DREF *MY-VAR* VARIABLE>

Within DRef, the dref Subclasses form the basis of extending docstring, source-location and arglist. Outside DRef, PAX makes pax:document extensible through pax:document-object*, which has methods specialized on drefs.

Finally, existing definitions can be queried with definitions and dref-apropos:

(definitions 'dref-ext:locate*)
==> (#<DREF LOCATE* GENERIC-FUNCTION>
-->  #<DREF LOCATE* (METHOD NIL (GLOSSARY-TERM))>
-->  #<DREF LOCATE* (METHOD NIL (SECTION))>
-->  #<DREF LOCATE* (METHOD NIL (READTABLE))>
-->  #<DREF LOCATE* (METHOD NIL (PACKAGE))>
-->  #<DREF LOCATE* (METHOD NIL (ASDF/SYSTEM:SYSTEM))>
-->  #<DREF LOCATE* (METHOD NIL (CLASS))>
-->  #<DREF LOCATE* (METHOD NIL (METHOD))>
-->  #<DREF LOCATE* (METHOD NIL (GENERIC-FUNCTION))>
-->  #<DREF LOCATE* (METHOD NIL (FUNCTION))>
-->  #<DREF LOCATE* (METHOD (:AROUND) (T))>
-->  #<DREF LOCATE* (METHOD NIL (T))> #<DREF LOCATE* (METHOD NIL (XREF))>
-->  #<DREF LOCATE* (METHOD NIL (DREF))>)
(dref-apropos 'locate-error :package :dref :external-only t)
==> (#<DREF LOCATE-ERROR CONDITION> #<DREF LOCATE-ERROR FUNCTION>)

(dref-apropos "ate-err" :package :dref :external-only t)
==> (#<DREF LOCATE-ERROR CONDITION> #<DREF LOCATE-ERROR FUNCTION>)

λ

2 Locatives and References

After the Introduction, here we get into the details. Of special interest are:

Operations (arglist, docstring, source-location) know how to deal with references (discussed in the Extending DRef).

λ

3 Listing Definitions

λ

4 Operations

The following functions take a single object definition as their argument. They may try to locate the definition of the object, which may signal a locate-error condition.

λ

5 Locative Types

The following are the locative types supported out of the box. As all locative types, they are named by symbols. When there is a CL type corresponding to the reference's locative type, the references can be resolved to a unique object as is the case in

(resolve (dref 'print 'function))
==> #<FUNCTION PRINT>

Even if there is no such CL type, the arglist, the docstring, and the source-location of the defining form is usually recorded unless otherwise noted.

λ

5.1 Locatives for Variables

λ

5.2 Locatives for Macros

λ

5.3 Locatives for Functions

λ

5.4 Locatives for Types and Declarations

λ

5.5 Locatives for the Condition System

λ

5.6 Locatives for Packages and Readtables

λ

5.7 DRef Locatives

λ

6 Glossary

λ

7 Extending DRef

λ

7.1 References

The following convenience functions are compositions of {locative-type, locative-args} and {xref-locative, dref-locative}.

λ

7.2 Adding New Locatives

Let's see how to tell DRef about new kinds of definitions through the example of the implementation of the class locative. Note that this is a verbatim pax:include of the sources. Please ignore any internal machinery. The first step is to define the locative type:

(define-locative-type class ()
  "Naturally, CLASS is the locative type for [CLASS][class]es.
  [CONDITIONs][type] may be referred to as CLASSes:

  ```cl-transcript
  (dref 'locate-error 'class)
  ==> #<DREF LOCATE-ERROR CONDITION>
  ```")

Next, we define a subclass of dref associated with the class locative type and specialize locate*:

(define-definition-class class class-dref)

(defmethod locate* ((class class))
  (make-instance 'class-dref :name (class-name class) :locative 'class))

(defmethod dref* (symbol (locative-type (eql 'class)) locative-args)
  (check-locative-args class locative-args)
  (unless (and (symbolp symbol)
               (find-class symbol nil))
    (locate-error "~S does not name a class." symbol))
  (make-instance 'class-dref :name symbol :locative 'class))

The first method makes (locate (find-class 'dref)) work, while the second is for (dref 'dref 'class). Naturally, for locative types that do not define first-class objects, the first method cannot be defined.

Then, with add-dref-actualizer, we install a function that that runs whenever a new dref is about to be returned from locate and turn the locative type into the locative class if the denoted definition is of a class:

(defun actualize-type-to-class (dref)
  (when (eq (dref-locative-type dref) 'type)
    (dref (dref-name dref) 'class nil)))

(add-dref-actualizer 'actualize-type-to-class)

Finally, we define a resolve* method to recover the class object from a class-dref. We also specialize docstring* and source-location*:

(defmethod resolve* ((dref class-dref))
  (find-class (dref-name dref)))

(defmethod docstring* ((class class))
  (documentation* class t))

(defmethod source-location* ((dref class-dref))
  (swank-source-location* (resolve dref) (dref-name dref) 'class))

We took advantage of having just made the class locative type being resolveable, by specializing docstring* on the class class. source-location* was specialized on class-dref to demonstrate how this can be done for non-resolveable locative types.

Classes have no arglist, so no arglist* method is needed. In the following, we describe the pieces in detail.

λ

7.3 Symbol Locatives

Let's see how the opaque define-symbol-locative-type and the obscure define-definer-for-symbol-locative-type macros work together to simplify the common task of associating definition with a symbol in a certain context.

λ

7.4 dref Subclasses

These are the dref subclasses corresponding to Locative Types. They are exported to make it possible to go beyond the standard Operations (e.g. pax:document-object*) and for subclassing.

for Variables

for Macros

for Functions

for Types and Declarations

for the Condition System

for Packages and Readtables

for DRef Locatives

λ

7.5 Source Locations

These represent the file or buffer position of a defining form and are returned by the source-location function. For the details, see the Elisp function slime-goto-source-location.