From: Justin Paston-Cooper
Subject: Questions about CLOS
Date: 
Message-ID: <1185638673.848882.141900@r34g2000hsd.googlegroups.com>
Hello. I'm just learning about CLOS. I would like to ask how I could
get a certain method to be called on any invocation of a method that
is called of a certain class. Thanks a lot in advance for any helpful
answers.

From: Alan Crowe
Subject: Re: Questions about CLOS
Date: 
Message-ID: <86lkd05nf6.fsf@cawtech.freeserve.co.uk>
Justin Paston-Cooper <·······@gmail.com> writes:

> Hello. I'm just learning about CLOS. I would like to ask how I could
> get a certain method to be called on any invocation of a method that
> is called of a certain class. Thanks a lot in advance for any helpful
> answers.

Maybe you want an around method that uses call-next-method? It goes like this:

CL-USER> (defgeneric foo (x))
#<STANDARD-GENERIC-FUNCTION FOO (0) {48982599}>

CL-USER> (defclass foundation ()())
#<STANDARD-CLASS FOUNDATION {4898A40D}>

CL-USER> (defclass basement (foundation)())
#<STANDARD-CLASS BASEMENT {48993AFD}>

CL-USER> (defclass ground-floor (basement)())
#<STANDARD-CLASS GROUND-FLOOR {4899D67D}>

CL-USER> (defmethod foo ((x foundation))
           (print 'foundation))
#<STANDARD-METHOD FOO (FOUNDATION) {48A08B4D}>

CL-USER> (defmethod foo ((x basement))
           (print 'basement))
#<STANDARD-METHOD FOO (BASEMENT) {48A71755}>

CL-USER> (defmethod foo ((x ground-floor))
           (print 'ground-floor))
#<STANDARD-METHOD FOO (GROUND-FLOOR) {48ADA5C5}>

CL-USER> (defmethod foo :around ((x foundation))
           (print 'always-run)
           (call-next-method))
#<STANDARD-METHOD FOO :AROUND (FOUNDATION) {4802A995}>

CL-USER> (dolist (class '(foundation basement ground-floor))
           (foo (make-instance class))
           (format t  "~&---------------"))


ALWAYS-RUN 
FOUNDATION 
---------------
ALWAYS-RUN 
BASEMENT 
---------------
ALWAYS-RUN 
GROUND-FLOOR 
---------------
NIL
 
Alan Crowe
Edinburgh
Scotland
From: Rainer Joswig
Subject: Re: Questions about CLOS
Date: 
Message-ID: <joswig-FD2B83.21291928072007@news-europe.giganews.com>
In article <························@r34g2000hsd.googlegroups.com>,
 Justin Paston-Cooper <·······@gmail.com> wrote:

> Hello. I'm just learning about CLOS.

Do you have a book for learning?

> I would like to ask how I could
> get a certain method to be called on any invocation of a method that
> is called of a certain class.

Methods don't belong to classes. Methods are organized
in generic functions. Common Lisp supports multiple dispatch.
So a method can have more than one arg and dispatch is
over all these arguments.

> Thanks a lot in advance for any helpful
> answers.

Hmm, I really don't understand your question. Maybe you
can rephrase it? Do you have code that makes
your problem clear?

(defclass foo () ())

(defmethod bar ((a-foo foo))
   (princ "bar"))

The method BAR will always be used if you call the
generic function with an object of class foo.

(let ((object (make-instance 'foo)))
  (bar object))

or shorter

(bar (make-instance 'foo))


Now with two arguments:

(defmethod baz ((a-number number) (a-foo foo))
  (princ "baz1"))

(baz 42 (make-instance 'foo))

Does that help?

-- 
http://lispm.dyndns.org
From: Kent M Pitman
Subject: Re: Questions about CLOS
Date: 
Message-ID: <usl78cmxd.fsf@nhplace.com>
Rainer Joswig <······@lisp.de> writes:

> Methods don't belong to classes. Methods are organized
> in generic functions. Common Lisp supports multiple dispatch.
> So a method can have more than one arg and dispatch is
> over all these arguments.

I took his question to be about the MOP, though he may not have meant
it to be.  That is, how one might write a method on the
method-addition mechanism itself, noticing that a method was being
added that specialized on a given class and doing something, perhaps
some kind of trace operation.

It sounds like a possible but admittedly suspect request, especially
for someone just starting out and "just trying to learn", since unless
his teaching text is suggesting implementing aspect-oriented
programming as a good way to learn CLOS, it doesn't sound lke a training
exercise.

Perhaps the right question at this point is: "Why?"

Knowing the intended purpose of this might lead to a very different
discussion.  People who are new to a language are often assuming they
should do things in a certain way that may not apply.
From: Justin Paston-Cooper
Subject: Re: Questions about CLOS
Date: 
Message-ID: <1185670077.469272.128450@r34g2000hsd.googlegroups.com>
On Jul 28, 9:07 pm, Kent M Pitman <······@nhplace.com> wrote:
> Rainer Joswig <······@lisp.de> writes:
> > Methods don't belong to classes. Methods are organized
> > in generic functions. Common Lisp supports multiple dispatch.
> > So a method can have more than one arg and dispatch is
> > over all these arguments.
>
> I took his question to be about the MOP, though he may not have meant
> it to be.  That is, how one might write a method on the
> method-addition mechanism itself, noticing that a method was being
> added that specialized on a given class and doing something, perhaps
> some kind of trace operation.
>
> It sounds like a possible but admittedly suspect request, especially
> for someone just starting out and "just trying to learn", since unless
> his teaching text is suggesting implementing aspect-oriented
> programming as a good way to learn CLOS, it doesn't sound lke a training
> exercise.
>
> Perhaps the right question at this point is: "Why?"
>
> Knowing the intended purpose of this might lead to a very different
> discussion.  People who are new to a language are often assuming they
> should do things in a certain way that may not apply.

Hello. I'm trying to think of a way to implement interest rates on
bank accounts. Each time withdraw, credit or  is called, I would like
it to be checked whether interest should be added to the account. Or
maybe another solution is better.
From: Kent M Pitman
Subject: Re: Questions about CLOS
Date: 
Message-ID: <uk5sjdh7v.fsf@nhplace.com>
Justin Paston-Cooper <·······@gmail.com> writes:

> Hello. I'm trying to think of a way to implement interest rates on
> bank accounts. Each time withdraw, credit or  is called, I would like
> it to be checked whether interest should be added to the account. Or
> maybe another solution is better.

The domain description sounds odd to me, since my understanding of
banks is that interest is more typically computed by simply examining
the balance in an account on a given day (or possibly the average
balance over an interval) and then applying an explicit transaction.
Building it into the low-level mechanism is going to have some curious
issues that are independent of the language you code this in.

But basically, you want to do define some class for your account

(defclass account ()
  ((balance :initform 0 :initarg :initial-balance :accessor balance)))

And then write some methods

(defmethod credit ((account account) amount)
  (incf (balance account) amount))

(defmethod debit ((account account) amount)
  (decf (balance account) amount))

You could either embed your update-interest operation in those definitions
before the incf or decf, or you might want to write a :before method.

(defmethod credit :before ((account account) amount)
  (update-interest account))

However, the problem I mentioned is that you probably want some
dataflow that remembers when you last updated interest and that knows
how long it's been sitting in the account.  I don't know any bank that
posts interest that way, but I guess it's possible.  It just wasn't
clear to me how you want to provide that.

It also isn't clear to me that any kind of demand-driven interest rate
calculation can really work unless you also put in checks to make sure 
that no one ever goes back in time and injects a credit or debit (which
happens a lot to me as I enter credits/debits in the wrong order in 
Quickbooks).  If you do, I don't know what you'll use as your interval
against which to compute (or uncompute) interest.

But in any case, I guess you're just asking for notational help.
I assume this is just a toy example.  And this should get you started.

As to "enforcement", Lisp doesn't keep someone from violating your
abstraction.  You're expected to advertise which functions should be
used, via package exports, but someone who wants to violate your
abstraction is free to do so.  Packages are just ways of doing
structured naming in a concise way.
From: Ken Tilton
Subject: Re: Questions about CLOS
Date: 
Message-ID: <51Uqi.159$cM5.39@newsfe12.lga>
Justin Paston-Cooper wrote:
> On Jul 28, 9:07 pm, Kent M Pitman <······@nhplace.com> wrote:
> 
>>Rainer Joswig <······@lisp.de> writes:
>>
>>>Methods don't belong to classes. Methods are organized
>>>in generic functions. Common Lisp supports multiple dispatch.
>>>So a method can have more than one arg and dispatch is
>>>over all these arguments.
>>
>>I took his question to be about the MOP, though he may not have meant
>>it to be.  That is, how one might write a method on the
>>method-addition mechanism itself, noticing that a method was being
>>added that specialized on a given class and doing something, perhaps
>>some kind of trace operation.
>>
>>It sounds like a possible but admittedly suspect request, especially
>>for someone just starting out and "just trying to learn", since unless
>>his teaching text is suggesting implementing aspect-oriented
>>programming as a good way to learn CLOS, it doesn't sound lke a training
>>exercise.
>>
>>Perhaps the right question at this point is: "Why?"
>>
>>Knowing the intended purpose of this might lead to a very different
>>discussion.  People who are new to a language are often assuming they
>>should do things in a certain way that may not apply.
> 
> 
> Hello. I'm trying to think of a way to implement interest rates on
> bank accounts. Each time withdraw, credit or  is called, I would like
> it to be checked whether interest should be added to the account. Or
> maybe another solution is better.
> 

COBOL springs to mind. Go away. LISP is for AI, not interest rates.

kenny

-- 
http://www.theoryyalgebra.com/

"Algebra is the metaphysics of arithmetic." - John Ray

"As long as algebra is taught in school,
there will be prayer in school." - Cokie Roberts

"Stand firm in your refusal to remain conscious during algebra."
    - Fran Lebowitz

"I'm an algebra liar. I figure two good lies make a positive."
    - Tim Allen
From: Barry Fishman
Subject: Re: Questions about CLOS
Date: 
Message-ID: <m3lkczp8uf.fsf@barryfishman.embarqmail.com>
Justin Paston-Cooper <·······@gmail.com> writes:
> Hello. I'm trying to think of a way to implement interest rates on
> bank accounts. Each time withdraw, credit or  is called, I would like
> it to be checked whether interest should be added to the account. Or
> maybe another solution is better.

If one does direct slot accesses, I don't think there is any way of
doing it.

However, if an interest check was done as :before method of its
'balance' reader method, it seems to be sufficient.

I would think the correct interest accumulation is only needed
as a part of a some operation that checks the balance.  Otherwise,
the operation is not dependent on it, and the fact that the instance
has not actually been updated is of no concern.

The only issue is that, there may some other slots in the account
instance that are dependent on the balance, and updated when the balance
is changed.  Their reader methods might also need to do an interest
check.

This might cause a lot of interest checks, however, a simple check of
the date against a precomputed date of the next interest change
could be relatively inexpensive.
From: Ken Tilton
Subject: Re: Questions about CLOS
Date: 
Message-ID: <7F4ri.141$Cb4.28@newsfe12.lga>
Barry Fishman wrote:
> Justin Paston-Cooper <·······@gmail.com> writes:
> 
>>Hello. I'm trying to think of a way to implement interest rates on
>>bank accounts. Each time withdraw, credit or  is called, I would like
>>it to be checked whether interest should be added to the account. Or
>>maybe another solution is better.
> 
> 
> If one does direct slot accesses, I don't think there is any way of
> doing it.
> 
> However, if an interest check was done as :before method of its
> 'balance' reader method, it seems to be sufficient.

Wouldn't this be a good application of a relational database?

(Sorry, that is the only Dilbert cartoon I remember.)

hth, kenny

ps. Go. A. Way. k
From: Daniel Barlow
Subject: Re: Questions about CLOS
Date: 
Message-ID: <1185802992.11125.0@proxy01.news.clara.net>
Ken Tilton wrote:
> Wouldn't this be a good application of a relational database?

Mauve has the most RAM


-dan
From: Drew Crampsie
Subject: Re: Questions about CLOS
Date: 
Message-ID: <46afaa39$0$19486$88260bb3@free.teranews.com>
On Sun, 29 Jul 2007 00:47:57 +0000, Justin Paston-Cooper wrote:

> On Jul 28, 9:07 pm, Kent M Pitman <······@nhplace.com> wrote:
>> Rainer Joswig <······@lisp.de> writes:
>> > Methods don't belong to classes. Methods are organized in generic
>> > functions. Common Lisp supports multiple dispatch. So a method can
>> > have more than one arg and dispatch is over all these arguments.
>>
>> I took his question to be about the MOP, though he may not have meant
>> it to be.  That is, how one might write a method on the method-addition
>> mechanism itself, noticing that a method was being added that
>> specialized on a given class and doing something, perhaps some kind of
>> trace operation.
>>
>> It sounds like a possible but admittedly suspect request, especially
>> for someone just starting out and "just trying to learn", since unless
>> his teaching text is suggesting implementing aspect-oriented
>> programming as a good way to learn CLOS, it doesn't sound lke a
>> training exercise.
>>
>> Perhaps the right question at this point is: "Why?"
>>
>> Knowing the intended purpose of this might lead to a very different
>> discussion.  People who are new to a language are often assuming they
>> should do things in a certain way that may not apply.
> 
> Hello. I'm trying to think of a way to implement interest rates on bank
> accounts. Each time withdraw, credit or  is called, I would like it to
> be checked whether interest should be added to the account. Or maybe
> another solution is better.

IMO, the best solution (if the interest check only appears in those three
functions) would be something like:

(defmethod withdraw (account amount)
  (check-interest account amount)
   ...)

(defmethod credit (account amount)
  (check-interest account amount)
   ...)

If you had a lot of methods that may have to check interest, you could
always wrap the defmethod up in a macro. No CLOS needed.

However, CLOS can be useful here if you may have many such functions. This
is some fairly idiomatic CLOS code implementing the idea:

;; First we model accounts.
(defclass account ()
  ())

(defgeneric perform-account-action (account action &rest args)
  (:method (account (action (eql 'withdraw)) &rest args)
     ...)
  (:method (account (action (eql 'credit)) &rest args)
     ...))

(defun withdraw (account amount)
  (perform-account-action account 'withdraw amount))

(defun credit (account amount)
  (perform-account-action account 'credit amount))

;; Then a mixin class for checking interest (defclass interest-check-mixin
()
 ((check-interest-p :accessor account-checks-interest-p
                   :initarg :check-interest
                   :initform t)))

;; Could also be :before or :after or the primary method. ;; but you get
the idea.

(defmethod perform-account-action :around (account action &rest args)
  (check-interest account)
  (call-next-method)
  (do-some-more-interest-checks account))
    
    
;; Finally we define a class that incorporates the above

(defclass account-with-interest-checks (account interest-check-mixin)
  ())

You could, of course, avoid the mixin and just model the interest check as
part of the base account class, but this is more fun (and shows some
useful CLOS techniques). And again, wrapping the whole thing up in a macro
is an option, especially if there are a lot of account-actions.

Finally, if you really want to be perverse, you could use the MOP.

;; here is one method. Remeber that in CLOS, methods belong to
generic-functions, and generic functions are (meta-) objects themselves.

CL-USER> (use-package :closer-mop)
T
CL-USER> (defclass interest-function-class (standard-generic-function)
	   ()
	   (:metaclass funcallable-standard-class))

#<FUNCALLABLE-STANDARD-CLASS INTEREST-FUNCTION-CLASS> CL-USER>  (defmethod
compute-discriminating-function
	      ((function interest-function-class))
	   (lambda (&rest args)
	     (print :check-interest)
	     (apply (call-next-method) args)))
	   
#<STANDARD-METHOD COMPUTE-DISCRIMINATING-FUNCTION
(INTEREST-FUNCTION-CLASS) {B33ADB9}> CL-USER> (defgeneric withdraw
(account amount)
	   (:generic-function-class interest-function-class) (:method (account
	   amount)
	     (print :withdraw)))

#<INTEREST-FUNCTION-CLASS WITHDRAW (1)> CL-USER> (defgeneric credit
(account amount)
	   (:generic-function-class interest-function-class) (:method (account
	   amount)
	     (print :credit)))

#<INTEREST-FUNCTION-CLASS CREDIT (1)> CL-USER> (defclass account () ())

#<STANDARD-CLASS ACCOUNT>
CL-USER> (let ((account (make-instance 'account)))
	   (withdraw account 1)
	   (credit account 2.0))

:CHECK-INTEREST
:WITHDRAW
:CHECK-INTEREST
:CREDIT

:CREDIT
CL-USER>

Again, one could easily wrap this up in a macro. The reason i keep
mentioning the macro is that the actual choice of implementation could be
delayed with a proper macro.. a DEFINE-ACCOUNT-ACTION macro could expand
to any of these implementations.

Finally, in the great c.l.l tradition of giving noobs exactly what they
ask for, something like the following (barely tested) will do what you
want, that is to say it will print :CHECK-INTEREST before ANY method is
executed on a class. It is not portable, may not even work, and a horrible
idea, but it does show that this sort of thing is possible, if you want to
sit down with AMOP and pull your hair out for a while:

(use-package :closer-mop)

(defclass interest-check-method (standard-method) ())

(defclass interest-check-mixin ()
  ())

(defun check-interest-p (class)
  (member (find-class 'interest-check-mixin)
	  (class-direct-superclasses class)))

(defmethod compute-applicable-methods-using-classes :around ((gf
standard-generic-function) classes)
  (let ((methods (call-next-method)))
    (values
     (mapcar (lambda (method)
	       (if (and
		    (some 'check-interest-p (method-specializers method)) (not (typep
		    method 'interest-check-method)))
		   (let* ((method-class (generic-function-method-class gf)))
		     (remove-method gf method)
		     (multiple-value-bind (lambda initargs)
			 (make-method-lambda
			  gf
			  (class-prototype method-class)
			  `(lambda (&rest args)
			     (print :check-interest)
			     (funcall ,(method-function method) args nil))

			  nil)

		       (add-method gf (apply #'make-instance 'interest-check-method
					     :function (compile nil lambda)
					     :specializers (method-specializers method) :qualifiers
					     (method-qualifiers method) :lambda-list (method-lambda-list
					     method) initargs))))
		   method))
		   
	     methods)
     nil)))

Maybe some of our resident MOP experts will have something better for you,
as that's about the extent of my MOP-FU.

HTH,

drewc

-- 
Posted via a free Usenet account from http://www.teranews.com