From: TimK
Subject: New to Lisp 'setf' problem
Date: 
Message-ID: <1141228426.051429.198260@z34g2000cwc.googlegroups.com>
I'm completely new to Lisp and my first foray into CLOS has got me a
bit confused.  I'm hoping someone will explain the behavior I'm seeing
or point me to a good resource.

I'm trying to implement a class that has a hash table in a slot.  I
want to write an accessor that takes a parameter that's a key for a
lookup in the hash table.  Here's my question:

Given this class definition:
		(defclass day()
		  ((tasks
		    :initform (make-hash-table :test 'equal))))

and this accessor method:
		(defmethod task (key day)
		  (gethash key (slot-value day 'tasks)))

I don't see why this call succeeds:
		(setf (gethash "L" (slot-value *d* 'tasks)) "3:33")

whle trying to use 'setf' with the accessor method:
		(setf (task "L" *d*) "3:33")

causes this error:
	  FUNCTION: undefined function #1=(SETF TASK)
	   [Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]

	   Backtrace:
			  0: #<SPECIAL-OPERATOR FUNCTION>
			  1: #<SPECIAL-OPERATOR LET*>
			  2: #<SYSTEM-FUNCTION EVAL>
			  3: #<COMPILED-FUNCTION SWANK::EVAL-REGION>
			  4: #<COMPILED-FUNCTION SWANK::LISTENER-EVAL-1>
			  5: #<COMPILED-FUNCTION #:|273 275 (DEFINTERFACE
CALL-WITH-SYNTAX-HOOKS (FN) ...)-31-3-1-1|>
			  6: #<COMPILED-FUNCTION SWANK::CALL-WITH-BUFFER-SYNTAX>
			  7: #<COMPILED-FUNCTION SWANK:LISTENER-EVAL>
			  8: #<SYSTEM-FUNCTION EVAL>
			  9: #<COMPILED-FUNCTION SWANK::EVAL-FOR-EMACS-1>
			 10: #<COMPILED-FUNCTION #:|472 477 (DEFINTERFACE
CALL-WITH-DEBUGGER-HOOK (HOOK FUN) ...)-52-3-1-1|>
			 11: #<COMPILED-FUNCTION SWANK::EVAL-FOR-EMACS>
			 12: #<COMPILED-FUNCTION SWANK::READ-FROM-EMACS>
			 13: #<COMPILED-FUNCTION SWANK::HANDLE-REQUEST-1>
			 14: #<COMPILED-FUNCTION SWANK::CALL-WITH-CONNECTION-1>
			 15: #<COMPILED-FUNCTION SWANK::CALL-WITH-REDIRECTED-IO>
			 16: #<COMPILED-FUNCTION SWANK::MAYBE-CALL-WITH-IO-REDIRECTION>
			 17: #<COMPILED-FUNCTION SWANK::CALL-WITH-CONNECTION>
			 18: #<COMPILED-FUNCTION SWANK::HANDLE-REQUEST>
			 19: #<COMPILED-FUNCTION SWANK::SIMPLE-SERVE-REQUESTS-2>
			 20: #<COMPILED-FUNCTION SWANK::SIMPLE-SERVE-REQUESTS>
			 21: #<COMPILED-FUNCTION SWANK::SERVE-REQUESTS>
			 22: #<COMPILED-FUNCTION SWANK::SERVE-CONNECTION>
			 23: #<COMPILED-FUNCTION SWANK::SETUP-SERVER-SERVE>
			 24: #<COMPILED-FUNCTION SWANK::SETUP-SERVER>
			 25: #<COMPILED-FUNCTION SWANK:START-SERVER>
			 26: #<SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT> 2

From: Tayssir John Gabbour
Subject: Re: New to Lisp 'setf' problem
Date: 
Message-ID: <1141231643.238407.111300@z34g2000cwc.googlegroups.com>
TimK wrote:
> Given this class definition:
> 		(defclass day()
> 		  ((tasks
> 		    :initform (make-hash-table :test 'equal))))
>
> and this accessor method:
> 		(defmethod task (key day)
> 		  (gethash key (slot-value day 'tasks)))
>
> I don't see why this call succeeds:
> 		(setf (gethash "L" (slot-value *d* 'tasks)) "3:33")
>
> whle trying to use 'setf' with the accessor method:
> 		(setf (task "L" *d*) "3:33")
>
> causes this error:
> 	  FUNCTION: undefined function #1=(SETF TASK)
> 	   [Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]

SETF is a bit strange that way; it's just a macro which must maintain
the illusion of setting a "place". So one does something like:

(defmethod (setf task) (val key (day day))
  (setf (gethash key (slot-value day 'tasks))
        val))


At the REPL:

CL-USER> (describe (slot-value *d* 'tasks))
  #<HASH-TABLE :TEST EQUAL :COUNT 0 {11469691}> is an EQUAL hash table.
  It holds 0 key/value pairs.
  ; No value

CL-USER> (setf (task "L" *d*) "3:33")
  "3:33"

CL-USER> (describe (slot-value *d* 'tasks))
  #<HASH-TABLE :TEST EQUAL :COUNT 1 {11469691}> is an EQUAL hash table.
  It holds 1 key/value pair: ("L" "3:33")
  ; No value



"SETF functions" are discussed here:
http://www.gigamonkeys.com/book/object-reorientation-classes.html
http://cooking-with-lisp.blogspot.com/2006/02/how-to-call-setf-method-directly.html

That 2nd link seems to show how poorly documented this seems to be.


Tayssir
From: Frank Buss
Subject: Re: New to Lisp 'setf' problem
Date: 
Message-ID: <192d7sglrssd8$.4m6dowggcv98$.dlg@40tude.net>
Tayssir John Gabbour wrote:

> (defmethod (setf task) (val key (day day))
>   (setf (gethash key (slot-value day 'tasks))
>         val))

that's nice, I didn't know that you can specify "setf" for a method. This
is better than my solution with defsetf, because it will work with
inheritance, too, I think.

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: jayessay
Subject: Re: New to Lisp 'setf' problem
Date: 
Message-ID: <m3bqwq3ua5.fsf@rigel.goldenthreadtech.com>
"TimK" <······@mailhaven.com> writes:


> whle trying to use 'setf' with the accessor method:
> 		(setf (task "L" *d*) "3:33")
> 
> causes this error:

You have not defined a (setf task) method that the macro setf can use
to determine the proper place to set.

In this case you could define it:

(defun (setf task) (val key) ...)

In general see DEFSETF and DEFINE-SETF-EXPANDER

But for this sort of case I think the following is better:


(defclass day()
  ((tasks
    :accessor tasks ; <-- defines an accessor method
    :initarg :tasks
    :initform (make-hash-table :test 'equal))))


(defmethod get-task ((self day) key)
  (gethash key (tasks self))) ; note: no need for slot-value

(defmethod set-task ((self day) key value)
  (setf (gethash key (tasks self)) value))


(defparameter *day* (make-instance 'day))

(set-task *day* :one 1)
=> 1
(get-task *day* :one)
=> 1
   t


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: Frank Buss
Subject: Re: New to Lisp 'setf' problem
Date: 
Message-ID: <16865yzyu9nvn$.1d7xeiprtzi8x.dlg@40tude.net>
TimK wrote:

> and this accessor method:
> 		(defmethod task (key day)
> 		  (gethash key (slot-value day 'tasks)))

I'm no CLOS expert, but this looks strange for a defmethod declaration.

> causes this error:
> 	  FUNCTION: undefined function #1=(SETF TASK)
> 	   [Condition of type SYSTEM::SIMPLE-UNDEFINED-FUNCTION]

I think the reason is because there is no function defined to change the
"place" for the value returned by the method. This works:

(defclass day()
  ((tasks
    :initform (make-hash-table :test 'equal))))

(defmethod task ((self day) key)
  (gethash key (slot-value self 'tasks)))

(defsetf task (day key) (value)
  `(setf (gethash ,key (slot-value ,day 'tasks)) ,value))

CL-USER > (defparameter monday (make-instance 'day))
MONDAY

CL-USER > (setf (task monday "L") "3:33")
"3:33"

CL-USER > (task monday "L")
"3:33"
T

-- 
Frank Buss, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Alan Crowe
Subject: Re: New to Lisp 'setf' problem
Date: 
Message-ID: <86u0aiknzy.fsf@cawtech.freeserve.co.uk>
"TimK" <······@mailhaven.com> writes:

> I'm completely new to Lisp and my first foray into CLOS has got me a
> bit confused.  I'm hoping someone will explain the behavior I'm seeing
> or point me to a good resource.
> 
> I'm trying to implement a class that has a hash table in a slot.  I
> want to write an accessor that takes a parameter that's a key for a
> lookup in the hash table.  Here's my question:
> 
> Given this class definition:
> 		(defclass day()
> 		  ((tasks
> 		    :initform (make-hash-table :test 'equal))))
> 
> and this accessor method:
> 		(defmethod task (key day)
> 		  (gethash key (slot-value day 'tasks)))
> 
> I don't see why this call succeeds:
> 		(setf (gethash "L" (slot-value *d* 'tasks)) "3:33")
> 
> whle trying to use 'setf' with the accessor method:
> 		(setf (task "L" *d*) "3:33")
> 
> causes this error:
> 	  FUNCTION: undefined function #1=(SETF TASK)

The issue here is the difference between accessors and
functions.

When you are learning Lisp, you find 3.1.2.1.2 "Conses as
Forms" and it looks straight forward, with four cases

     Special forms eg (let ...)

     Macro forms eg (loop ...)

     Fuction forms eg (sin ...)

     Lambda forms eg ((lambda (x y)(+ x y)) 5 7) => 12

but when you look in, for example, the sequences dictionary:

     System Class SEQUENCE

     Function COPY-SEQ

     Accessor ELT

     Function FILL

     Function MAKE-SEQUENCE

     Accessor SUBSEQ

     ...

What are accessors? Often they are pairs of functions, one
to read data out of a data structure, and one to write data
into a data structure. However the essential point of an
accessor is that there is one function, f, to read data out of a
data structure, and that setf knows what to do when it
is part of

(setf (f x y) z)

In one sense, setf always knows what to do. If none of the first
eight rules in "5.1.2 Kinds of Places" apply, setf can always
use rule 9 and expand to

(funcall (function (setf f)) z x y)

Here (setf f) is just a function name, the partner to f, and
setf does not check whether it exists yet.

(if x,y, and z are function calls then setf will do
something fancier to make sure that they are called in the
right order)

However there had better be a (defun (setf f) (z x y) ...)
or a (defmethod (setf f) ((....))) executed before the
(funcall (function (setf f)) z x y) gets run or the function
named (setf f) will not be found.

The issue is "how do I roll my own accessors?"

You need a (defun f (args) body1) to define the
reader. You've already got that.

You also need a (defun (setf f) (new-value args) body2) to
define the writer.

Example:

CL-USER> (defun middle (seq)
           (elt seq (floor (length seq) 2)))
MIDDLE

CL-USER> (defparameter *s* (list 0 1 2 3 4))
*S*

CL-USER> (middle *s*)
2

CL-USER> (defun (setf middle) (new-value seq)
           (setf (elt seq (floor (length seq) 2))
                 new-value))
(SETF MIDDLE)

CL-USER> (setf (middle *s*) 'two)
TWO

CL-USER> *s*
(0 1 TWO 3 4)

Accessors are not specific to CLOS. However it is common to
partition a CL program so that much of it is written in a
functional style, and writers are not required in that
part. The remaining parts, which change data structures,
require greater care, because of the risk of bugs due to
errors with shared structures. CLOS is often used to help
organise this code. Hence the phenomenon that you have
encountered: you first bump into accessors when you try to
learn CLOS.

Alan Crowe
Edinburgh
Scotland

P.S. You can find the hyper spec here

http://www.lisp.org/HyperSpec/Body/sec_3-1-2-1-2.html

but I always worry about directing beginners to the
specification. It has examples, so it looks as though it
contains tutorial material, but sometimes they are very hard
examples illustrating tricky points for the benefit of
programmers who have had a lot of experience with previous
dialects of Lisp.