Skip to content


Repository files navigation


This library lets you build a metaclass which in turn lets you specify extra slot options in its classes. Options may be easily inspected and custom inheritence may be set up. Metaobject Protocol (MOP) is used for the implementation - through closer-mop. Some convenience function for processing slot options are also available.

Possible use case: you want to automatically set up some definitions based on some slots, but you want to have control over it right in the class definition (check out Macrobuilding below).


So, the first step is to define the metaclass and describe what options you want to have.

(def-extra-options-metaclass example-metaclass ()
  ((start :initform nil ; `:coalescence', by default, is `replace-or-inherit'
          :type integer)
   (validates :coalescence bound-only-once
              :type boolean)))

NOTE: if you plan on instantiating objects of the metaclass in the same file you define that metaclass, ensure to wrap the metaclass definition in an eval-always macro (part of serapeum), otherwise you will get errors when compiling the file.

For each option, other than its name, you can specify:

  • type,
  • initform (if not set, the option’s value will be unbound, as expected) and
  • coalescence (see Inheritence and Coalescence below).

Now, a class with this metaclass will have :start and :validates options:

(defclass alpha ()
  ((zulu :initform 22
         :start 55
         :validates t))
  (:metaclass example-metaclass))

Before you can inspect the class, finalize it first (or simply instantiate its object): (c2mop:ensure-finalized (find-class 'alpha)). Now, you can access the slot definition:

(let ((zulu-definition (find 'zulu (c2mop:class-slots (find-class 'alpha))
                             :key #'c2mop:slot-definition-name)))
  (values (slot-definition-start zulu-definition)
          (slot-definition-validates zulu-definition)))
55, T

As you can see, the def-extra-options-metaclass has defined slot-definition-start and slot-definition-validates.

Inheritence and Coalescence

When inheriting classes, you can control how options will be inherited. To this end, you can specialize on coalesce-options. These specializations are available out of the box:

  • replace-or-inherit (default, simply inherits the value from its most direct superclass)
  • bound-only-once (once specified, can’t be overriden in subclasses)
  • merge (lists only, new value merged with all the values of the superclasses)
  • difference (lists only, set-difference of new value with all values of the superclasses)

For a demonstration, let’s inherit from alpha:

(defclass beta (alpha)
  ((zulu :start 66))
  (:metaclass example-metaclass))

Now, if you inspect beta, you will see that start is now 66. Note that you can’t set :validates to NIL, as that would cause an error in coalesce-options, because :coalesecence for this option is bound-only-once. Also, given that the compiler does type checking (like SBCL with high enough safety), trying to supply a value of a non-compatible type to an option will yield a compile-time error. See example.lisp to see all of these in action.


Now, if you want to write a macro which defines a class and inspects it right away (to build custom definitions based on it), you can do something like this:

(defmacro def-your-thing (name direct-superclasses direct-slots &rest options)
     ;; you will get errors on the first run if you don't have this `eval-when':
     (eval-when (:compile-toplevel :load-toplevel :execute) 
       (defclass ,name ,direct-superclasses
         (:metaclass your-metaclass)
       (c2mop:ensure-finalized (find-class ',name)))
     (your-macro-that-inspects-the-class ,name)))

The downside is that this relies on immediate finalization of the class. If you don’t want that for some reason, you could probably try defining an after method for c2mop:ensure-class-using-class, but that doesn’t work out in some cases.

Convenience functions

See the docstrings for details.

Finding and Collecting Slots

find-slotfind effective slot name in class/class-name
find-dslotsame as find-slot, but for direct-slots
all-direct-slot-definitionsyields all slot definitions from the class precedence list (which includes the class itself).
all-slot-readers / all-slot-writersuse all-direct-slot-definitions to yield all readers/writers from the class precedence list.

Slot definitions

pick-in-slot-defgiven option key, finds all values in a slot definition.
pick-in-slot-defsas pick-in-slot-def but for many slots.
remove-from-slot-defgiven an option key, removes it from a slot definition.
ensure-option-in-slot-defmakes sure that a slot option is present in a slot definition and puts it there (with a default) otherwise.

Change detection

slot-option-effective-changed-psee if the newest state of option changed, useful to see if it’s necessary to, say, define a new method or if the inherited one is sufficient (see the docstring)
slot-option-direct-changed-psame as slot-option-effective-changed-p, but for direct slots


ensure-finalized-precedenceensures that all classes in the precedence list are finalized

Code Guidelines

Less than 80 chars per line.


Available on Quicklisp:

(ql:quickload :slot-extra-options)

To run tests, do:

(asdf:test-system :slot-extra-options)

Compiler Support

Should work where closer-mop works.

Tests ran successfully on: SBCL 2.0.11, ECL 20.4.24, CCL 1.12.




Extra options for slots using MOP.




LGPL-3.0, GPL-3.0 licenses found

Licenses found






No releases published


No packages published