This project implements a class system for Lisp through a set of macros. The implemented features are:
- Classes
- Default Slot Initializers
- Multiple Inheritance
- Generics
- Multiple-Dispatch Methods
- Auxiliary Methods
The def-class macro allows you to create a class complete with default constructor, getters, setters, recognizers. For instance, to create a class person with slots name and age and respective default methods do:
> (def-class person name age)
(...)
> (setf p (make-person :age 50 :name John))
#S(...)
> (person-age p)
50
> (set-person-name p "Bill")
"Bill"
> (person-name p)
"Bill"
> (person? p)
t
It's possible to provide default initializer forms to determine the value of a form if none is provided:
> (def-class person name age :initform (+ 10 1))
t
> (setf p (make-person :name "John"))
#S(...)
> (person-age p)
11
> (person-name p)
"John"
The def-class macro also allows you to inherit from multiple classes:
> (def-class thing)
t
> (def-class being age)
t
> (def-class (person being thing) name)
t
> (setf p (make-person :name "John" :age "22"))
#S(...)
> (person-age p)
11
> (being-age p)
11
> (person-name p)
"John"
> (person? p)
t
> (being? p)
t
The def-generic macro allows you to specify a function's name and parameters, omitting the implementation. This functionality is complemented by multiple-dispatch methods, described in the next section.
> (def-generic sum (p1 p2))
SUM
The def-method macro allows you to specify a generic function's implementation for a specific set of parameter specializers:
> (def-method sum ((person p1) (person p2)
(format t "Adding two people's ages~%")
(+ (person-age p1) (person-age p2)))
SUM
> (def-method sum ((person p1) (being b2))
(format t "Adding a person and a being's age~%")
(+ (person-age p1) (being-age b2)))
SUM
> (sum (make-person :age 1) (make-person :age 2))
"Adding two people's ages"
3
> (sum (make-person :age 1) (make-being :age 2))
"Adding a person and a being's age"
3
Note that in the first example, both methods would be applicable since person is a subclass of being. However, the class system invokes the most specific of all the applicable methods. This is the coherent with the standard method combination of the Common Lisp Object System.
The def-method macro also allows you to specify auxiliary methods that are to be called either before or after the primary method. Like its counterpart in Common Lisp, this feature calls every applicable auxiliary method, not just the most specific:
> (def-method sum :before ((person p1) (person p2))
(format t "Before adding~%"))
SUM
> (def-method sum :after ((person p1) (person p2))
(format t "After adding~%"))
SUM
> (sum (make-person :age 1) (make-person :age 2))
"Before adding"
"After adding"
The returned value of the effective method composed of auxiliary and primary methods is the returned value of the primary method. This example showcases one possible usage of the method combination:
> (def-generic sum (p1 p2))
SUM
> (def-method sum ((person p1) (person p2))
(format t "Adding people's ages~%")
(+ (person-age p1) (person-age p2)))
SUM
> (def-method sum :before ((person p1) (person p2))
(format t "Before adding~%"))
SUM
> (def-method sum :after ((person p1) (person p2))
(format t "After adding~%"))
SUM
> (def-method sum :after ((person p1) (person p2))
(format t "Again after adding~%"))
SUM
> (sum (make-person :age 1) (make-person :age 2))
"Before adding"
"Adding people's ages"
"After adding"
"Again after adding"
3