I'm playing around with a roguelike and have a class monster like such:
(defclass monster ()
((hit-points
:accessor hit-points
:initform 1
:initarg :hit-points)))
I'm trying to kill a monster when its hitpoints reach 0:
(defmethod (setf hit-points) ((monster monster) hp)
(when (<= hp 0)
(error "got this far")))
But it never gets to the error!
>> (let ((monster (make-instance 'monster)))
(setf (hit-points monster -1)))
-1
I'm sure that this is a fairly basic thing, but it sure doesn't make
sense to me.
--
Robert Uhl <http://public.xdi.org/=ruhl>
Aquarion> Naming scheme is simple. You set it up, you name it.
GB> I like that. That's so... so... honourable.
--on host naming
Den Sat, 02 Feb 2008 13:35:58 -0700 skrev Robert Uhl:
> I'm playing around with a roguelike and have a class monster like such:
>
> (defclass monster ()
> ((hit-points
> :accessor hit-points
> :initform 1
> :initarg :hit-points)))
>
> I'm trying to kill a monster when its hitpoints reach 0:
>
> (defmethod (setf hit-points) ((monster monster) hp)
> (when (<= hp 0)
> (error "got this far")))
>
> But it never gets to the error!
>
> >> (let ((monster (make-instance 'monster)))
> (setf (hit-points monster -1)))
> -1
>
> I'm sure that this is a fairly basic thing, but it sure doesn't make
> sense to me.
Watch your syntax.
cl-user> (let ((monster (make-instance 'monster)))
(setf (hit-points monster -1)))
Execution of a form compiled with errors.
Form:
(setf (hit-points monster -1))
Compile-time error:
(in macroexpansion of (setf #))
(hint: For more precise location, try *BREAK-ON-SIGNALS*.)
odd number of args to SETF
[Condition of type sb-int:compiled-program-error]
Restarts:
0: [abort] Return to SLIME's top level.
1: [terminate-thread] Terminate this thread (#<thread "repl-
thread" {AFDA999}>)
That's one (trivial) thing. The other is a combination of two bugs:
1. Proper method signature is
(defmethod (setf hit-points) (hp (monster monster)) ...)
2. You're using :accessor, which provides a standard default writer,
masking the incorrect signature of your method above. If you used
just :reader, you'd get "no applicable method" error together with
arguments passed, which should've given you enough hints
In general, it's a good idea not to use :accessor if you're gonna provide
an interesting writer yourself, precisely to avoid masking bugs such as
yours.
Cheers,
Maciej
Robert Uhl <·········@NOSPAMgmail.com> writes:
> (defmethod (setf hit-points) ((monster monster) hp)
> (when (<= hp 0)
> (error "got this far")))
You've got 2 definitions for (setf hit-points) ... your defclass also
defines one with the :accessor.
Probably using :after would work (not tested).
(defmethod (setf hit-points) :after ((monster monster) hp)
(when (<= hp 0)
(error "got this far")))
Joost.
Robert Uhl wrote:
> I'm playing around with a roguelike and have a class monster like such:
>
> (defclass monster ()
> ((hit-points
> :accessor hit-points
> :initform 1
> :initarg :hit-points)))
>
> I'm trying to kill a monster when its hitpoints reach 0:
>
> (defmethod (setf hit-points) ((monster monster) hp)
> (when (<= hp 0)
> (error "got this far")))
You probably want a :before or :after method here. What's more
important, though, is that you've got the argument order wrong: In setf
functions, the new value is always the first parameter.
Pascal
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Pascal Costanza <··@p-cos.net> writes:
>
> You probably want a :before or :after method here.
Somehow I managed to elide that in my copy-paste...
> What's more important, though, is that you've got the argument order
> wrong: In setf functions, the new value is always the first parameter.
That as it. A weird convention, given that one write (setf place
thing), but I suppose I'll get used to it eventually.
No doubt there's a very good reason it's backwards.
--
Robert Uhl <http://public.xdi.org/=ruhl>
The light at the end of the tunnel is not an oncoming train; it is
muzzle-flash. --Peter Coffin
On Sun, 03 Feb 2008 00:33:37 -0700, Robert Uhl wrote:
> Pascal Costanza <··@p-cos.net> writes:
>>
>> You probably want a :before or :after method here.
>
> Somehow I managed to elide that in my copy-paste...
>
>> What's more important, though, is that you've got the argument order
>> wrong: In setf functions, the new value is always the first parameter.
>
> That as it. A weird convention, given that one write (setf place
> thing), but I suppose I'll get used to it eventually.
>
> No doubt there's a very good reason it's backwards.
Damn right there is.. &optional, &rest and &key.
Cheers,
drewc
--
Posted via a free Usenet account from http://www.teranews.com
Robert Uhl wrote:
> Pascal Costanza <··@p-cos.net> writes:
>> You probably want a :before or :after method here.
>
> Somehow I managed to elide that in my copy-paste...
>
>> What's more important, though, is that you've got the argument order
>> wrong: In setf functions, the new value is always the first parameter.
>
> That as it. A weird convention, given that one write (setf place
> thing), but I suppose I'll get used to it eventually.
>
> No doubt there's a very good reason it's backwards.
Yes: In Common Lisp, functions can declare to have an arbitrary number
of arguments. This means that you cannot put the new-value argument at
the end. Other choices could have been made, but putting it at the front
is the most straightforward way out.
Pascal
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
Robert Uhl <·········@NOSPAMgmail.com> writes:
> Pascal Costanza <··@p-cos.net> writes:
> >
> > You probably want a :before or :after method here.
>
> Somehow I managed to elide that in my copy-paste...
>
> > What's more important, though, is that you've got the argument order
> > wrong: In setf functions, the new value is always the first parameter.
>
> That as it. A weird convention, given that one write (setf place
> thing), but I suppose I'll get used to it eventually.
>
> No doubt there's a very good reason it's backwards.
There is. And understanding the reason makes it easier to remember why
it is.
The reason the order is backwards is because there is always exactly one
new value, but there can be be additional other arguments to the
accessor, including an unlimited number
Consider the case of AREF, which can take an arbitrary number of
indices, depending on the number of array dimensions. So, it makes more
sense to have the value come first, since that would allow one to write
something like:
(defmethod (setf aref) (new-value &rest indices) ...)
instead of forcing one to grab the last or a variable number of
arguments in order to get the new-value.
--
Thomas A. Russ, USC/Information Sciences Institute
P� Sat, 02 Feb 2008 21:35:58 +0100, skrev Robert Uhl
<·········@NOSPAMgmail.com>:
> I'm playing around with a roguelike and have a class monster like such:
>
> (defclass monster ()
> ((hit-points
> :accessor hit-points
> :initform 1
> :initarg :hit-points)))
>
> I'm trying to kill a monster when its hitpoints reach 0:
>
> (defmethod (setf hit-points) ((monster monster) hp)
> (when (<= hp 0)
> (error "got this far")))
>
> But it never gets to the error!
>
> >> (let ((monster (make-instance 'monster)))
> (setf (hit-points monster -1)))
> -1
>
> I'm sure that this is a fairly basic thing, but it sure doesn't make
> sense to me.
>
(defclass monster ()
((hit-points
:accessor hit-points
:initform 1
:initarg :hit-points)))
(defmethod (setf hit-points) :before (hp (monster monster))
(when (<= hp 0)
(error "got this far")))
You swapped the order of the params in the setf specializer.
Note I leave the assignment to the accessor but chain the method to run
:before so the check gets done.
--------------
John Thingstad