I’m working my way through porting some of my older stuff to uLisp. Since I like object oriented programming, I’m a happy user of the ingenious simple object system ULOS. Still it had one drawback to me which I tried to solve: It didn’t feature constructors, i.e. one would have to populate properties for each and every object “instance” individually when creating it. Thus I tried to implement a kind of constructor mechanism myself and it seems to work, so I thought I’d share what I did, maybe it’s considered helpful by others.
My apologies for possible logic mistakes, I’m still very new to Lisp. 2nd apology for renaming “object” to “class” in the following code as well as renaming “value” to “getv” and “update” to “setv”. I did that because it helps me to read the code more like I’m used to in Python or C (think “getter” and “setter” methods). The same applies to my parenthese formatting. Now here’s the slightly extended code:
; Define a class
(defun class (&optional parent slots constructor)
(let ((obj (when parent (list (cons 'parent parent)))))
(when (and constructor parent)
(when (symbolp parent) (setq parent (eval parent)))
(loop
(when (null parent) (return parent))
(push (first parent) obj)
(setq parent (cdr parent)))
)
(loop
(when (null slots) (return obj))
(push (cons (first slots) (second slots)) obj)
(setq slots (cddr slots)))
)
)
; Get the value of a slot in an instance/class or its parents
(defun getv (obj slot)
(when (symbolp obj) (setq obj (eval obj)))
(let ((pair (assoc slot obj)))
(if pair (cdr pair)
(let ((p (cdr (assoc 'parent obj))))
(and p (getv p slot))))
)
)
; Update a slot in an instance/class
(defun setv (obj slot value)
(when (symbolp obj) (setq obj (eval obj)))
(let ((pair (assoc slot obj)))
(when pair (setf (cdr pair) value))
)
)
; Add value and method slots (here obj needs to be passed quoted, i.e. by reference)
(defun add-prop (obj slots)
(let (newlist)
(loop
(when (null slots) (return))
(push (cons (first slots) (second slots)) newlist)
(setq slots (cddr slots)))
(set obj (append (eval obj) newlist))
)
)
Some further explanations:
- The “constructor” mechanism works by simply copying properties from an object a new one is derived from. This has to be done deliberately by setting a third parameter in the “class” function to “t” (true). If it’s omitted/nil, the properties are not copied (i.e. no “constructor” used).
- The mechanism keeps “class” and “instance” properties separated.
- It’s still possible to add individual properties to “instances” using the existing slot mechanism in the “class” (“object”) function, i.e. pass by/extend inheritance.
- The new fourth function “add-prop” provides a simplified way to add properties to any object anytime, comparable to the mechanism in JavaScript.