3 ms·
Circa 1984: (defclass person () ((name :initarg :name :accessor person-name) (age :initarg :age :accessor person-age))) (defmethod person-age
by mahmud 16y ago
Circa 1984:
(defclass person ()
((name :initarg :name :accessor person-name)
(age :initarg :age :accessor person-age)))
(defmethod person-age :after ((someone person))
(let* ((age (person-age someone))
(category (if (< age 16)
:child
(if (>= age 66)
:senior
:adult))))
(values age category)))
Instead of having some outside "ticket" function to categorize the age of a person, you should just instrument the generated PERSON-AGE accessor to add the extra info you need. That way you don't need to modify the rest of your system to use this data in non-standard ways. The accounting module, for example, can take care of senior citizen and kid discounts of tickets without having to be told about it. The system would extensible, without rewrite, once you want to add seasonal discounts and other business-requirement unapparent during development. (Looking into ContextL and Context Oriented Programming.)
Similary, print-name shouldn't exist as a standalone function for something so mundane. You should just augment the builtin PRINT-OBJECT method that specializes on the PERSON class to print suitable titles. You don't even have to instrument the main PRINT-OBJECT, just the one that prints the actual receipt to the paper-printing HARDCOPY stream.
(defmethod print-object :around ((someone person)
(stream hardcopy))
(when (next-method-p)
(let ((obj (call-next-method)))
...
...
(with-open-printer (lpr #p"/dev/printer")
(format lpr "..")))) ...
I mean, seriously; I hope we're not blogging back and forth about CLOS-101 applications of methods.
- deleted 16y ago[deleted]
- ekiru 16y agoI know that this is a pretty tame example of subset types. But do you really think that modifying the return values of person-age for everything in the system is more easily extensible than a subset type which is external to the objects? Suppose that somewhere else in the system, you want to dispatch based on whether a Person is old enough to legally drink alcohol. With subset types, you don't have to be aware of the Child/Adult/Senior partition at all. With the :after approach, you're dependent on the ordering of the after methods and you have to know about the existence of the :child/:senior/:adult test. Here's the code for buy-beer in Perl 6: subset CanDrink of Person where *.age >= 21; subset CannotDrink of Person where *.age < 21; multi buy-beer (CanDrink $person) { give $person, Beer.new } multi buy-beer (CannotDrink $kid) { call-cops-on $kid } No other code in the system need be aware of it. No other code in the system is affected by it.
- mahmud 16y agoSubset types, at least in that form you have shown, do not exist in Common Lisp. That's a very MLish feature, and Perl keeps on surprising me :-) Do another, more elaborate one when you have time. Would love to learn new things.
- lispm 16y agoPredicate dispatch in CLOS: ftp://publications.ai.mit.edu/ai-publications/2001/AITR-2001-006.pdf
- mahmud 16y agoWell, there goes Sunday. I was supposed to port a bunch of stuff to ABCL tomorrow, but I can't wait to fall asleep tonight and read that paper first thing in the morning. Wish I could fork multiple selves of me to devour all these goodies, Rainer :-) (mapcar (lambda (task) (bt:make-thread (lambda () (funcall (car task) (cdr task)))) (loop for me from 1 to *infinity* and problem in *all-problems* collecting (list me problem)))
- Hexstream 16y ago1. The values of :after methods are ignored. 2. You're calling person-age from within the method so you enter infinite recursion. What you were looking for is: (defmethod person-age :around ((someone person)) (let* ((age (call-next-method)) (category (if (< age 16) :child (if (>= age 66) :senior :adult)))) (values age category)))
- mahmud 16y agoyeah, I am on a pristine box without slime; and those written after a few beers late at night.