UP | HOME

Closer-mop — Portable MOP Access for Common Lisp

Table of Contents

Definition

closer-mop is a portability library for the Common Lisp Meta-Object Protocol (MOP). The MOP is a standard extension to CLOS that allows programs to introspect and modify the class system itself: querying slot definitions, class hierarchies, generic function dispatch, and so on. Different CL implementations expose the MOP with slightly different APIs; closer-mop smooths these differences.

Online documentation: github.com/pcostanza/closer-mop MOP reference: clos-mop.hexstruct.com

Functions Used in Tolk

closer-mop is used exclusively in tolk/utils to support defmethod-bind. The relevant functions are:

class-slots — all effective slots of a class

(closer-mop:class-slots (find-class 'num))
;; => (#<EFFECTIVE-SLOT-DEFINITION N>)

Returns the list of effective slot definitions for a class, including inherited slots. Each slot definition object carries the slot name, type, initargs, and any readers/writers.

class-slots requires the class to be finalised (see class-finalized-p and finalize-inheritance below).

class-direct-slots — slots defined directly on a class

Returns only the slots defined in the class's own defclass form, not inherited ones.

slot-definition-name — name of a slot definition

(closer-mop:slot-definition-name slot-def)  ; => :N  (or N, implementation-dependent)

slot-definition-readers — reader methods for a slot

(closer-mop:slot-definition-readers slot-def)  ; => (N-READER) or NIL

defmethod-bind uses this to decide whether to use (:accessors ...) or (:slots ...) in the generated bind call: if a reader exists, the accessor is more efficient (avoids slot-value lookup); if not, :slots is used.

class-finalized-p and finalize-inheritance

(unless (closer-mop:class-finalized-p class)
  (closer-mop:finalize-inheritance class))

A class is finalised when its effective slot list has been computed (taking inheritance into account). SBCL finalises classes automatically on first instantiation, but at macro-expansion time (inside defmethod-bind's eval-when) the class may not yet have been instantiated. finalize-inheritance forces this computation so that slot introspection works.

class-precedence-list — the class hierarchy in dispatch order

(closer-mop:class-precedence-list (find-class 'plus))
;; => (#<STANDARD-CLASS PLUS> #<STANDARD-CLASS ARITH-BINARY-OP>
;;     #<STANDARD-CLASS AST-NODE> #<STANDARD-CLASS STANDARD-OBJECT> ...)

Used in tolk/utils class-readers to walk the inheritance chain and collect all reader methods, including those inherited from superclasses.

Why This Requires eval-when in Tolk

defmethod-bind is a macro that introspects class slot definitions at macro-expansion time to decide how to generate the bind body. For this to work, the class must be defined and finalised before the macro expands.

In tolk, AST classes are therefore wrapped in:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defclass ast-node () ())
  (defclass num (ast-node) ((n :type number :initarg :n)))
  (defclass arith-binary-op (ast-node) ...)
  (defclass plus (arith-binary-op) ...)
  (defclass mult (arith-binary-op) ...))

eval-when (:compile-toplevel ...) ensures the class definitions are evaluated when the compiler processes the file, not just when the file is loaded at runtime. Without this, defmethod-bind would try to introspect classes that do not yet exist in the compile-time environment, causing a "class not found" error.

See Defmethod-bind for the full macro and its use of closer-mop.

Loading

(ql:quickload :closer-mop)

Related