README.org
March 26, 2021 ยท View on GitHub
#+STARTUP: indent showall
- slot-extra-options
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 [[https://github.com/pcostanza/closer-mop][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][Macrobuilding]] below).
** Example
So, the first step is to define the metaclass and describe what options you want to have.
#+BEGIN_SRC lisp
(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)))
#+END_SRC
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 [[https://github.com/ruricolist/serapeum][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) andcoalescence(see [[*Inheritence and Coalescence][Inheritence and Coalescence]] below).
Now, a class with this metaclass will have :start and :validates options:
#+BEGIN_SRC lisp (defclass alpha () ((zulu :initform 22 :start 55 :validates t)) (:metaclass example-metaclass)) #+END_SRC
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:
#+BEGIN_SRC lisp (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))) #+END_SRC
#+BEGIN_SRC lisp 55, T #+END_SRC
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:
#+BEGIN_SRC lisp (defclass beta (alpha) ((zulu :start 66)) (:metaclass example-metaclass)) #+END_SRC
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 [[file:example.lisp]] to see all of these in action.
** Macrobuilding
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:
#+BEGIN_SRC lisp
(defmacro def-your-thing (name direct-superclasses direct-slots &rest options)
(progn ;; 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
,direct-slots
(:metaclass your-metaclass)
,@options)
(c2mop:ensure-finalized (find-class ',name)))
(your-macro-that-inspects-the-class ,name)))
#+END_SRC
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
| function | purpose |
|-------------------------------------+-----------------------------------------------------------------------------------------------|
| slot-exists-and-bound-p | self-explanatory |
| find-slot | find effective slot name in class/class-name |
| find-dslot | same as find-slot, but for direct-slots |
| all-direct-slot-definitions | yields all slot definitions from the class precedence list (which includes the class itself). |
| all-slot-readers / all-slot-writers | use all-direct-slot-definitions to yield all readers/writers from the class precedence list. |
*** Slot definitions
| function | purpose |
|---------------------------+-------------------------------------------------------------------------------------------------------------|
| pick-in-slot-def | given option key, finds all values in a slot definition. |
| pick-in-slot-defs | as pick-in-slot-def but for many slots. |
| remove-from-slot-def | given an option key, removes it from a slot definition. |
| ensure-option-in-slot-def | makes sure that a slot option is present in a slot definition and puts it there (with a default) otherwise. |
*** Change detection
| function | purpose |
|---------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| slot-option-effective-changed-p | see 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-p | same as slot-option-effective-changed-p, but for direct slots |
*** Finalization
| function | purpose |
|-----------------------------+---------------------------------------------------------------|
| ensure-finalized-precedence | ensures that all classes in the precedence list are finalized |
** Code Guidelines
Less than 80 chars per line.
** Installation
Available on Quicklisp:
#+BEGIN_SRC lisp (ql:quickload :slot-extra-options) #+END_SRC
To run tests, do:
#+BEGIN_SRC lisp (asdf:test-system :slot-extra-options) #+END_SRC
** Compiler Support
Should work where closer-mop works.
Tests ran successfully on: =SBCL 2.0.11=, =ECL 20.4.24=, =CCL 1.12=.
** Licence
LGPL-3.0-or-later