From: Will White
Subject: exercise problem
Date: 
Message-ID: <87d6f94hpd.fsf@will.white.ntlworld.com>
hi - I am trying to learn cl. This is what I did for an exercise in Nick Levine's
lecture notes (the end of week 7, as pointed to in somewhere like alu):

(defun make-student-db ()
  ;; store a crude database of students and modules in a closure
  ;; it is accessed by functions returned by values, to be used by e.g.
  ;; multiple-value-setq *clear* *update-students* .. etc
  (defstruct student
    name
    SID        ; an integer
    modules)
  (defstruct module
    title
    mid
    students)
  (let ((roll ())
	(high-sid 0)
	(high-mid 0)
	(modules ()))
	(flet ((prompt-for-student ()
			     (format t "~&~A~%" "Please enter a new student name or newline to end")
			     (read-line))
	       (prompt-for-module ()
			     (format t "~&~A~%" "Please enter a module name or newline to end")
			     (read-line))
	       (find-student (sid)
			     "Find a student from his sid"
			     (dolist (elem roll) (if (= (student-sid elem) sid) (return  elem))))
	       (find-module (mid)
			    "Find a module from its id"
			    (dolist (elem modules) (if (= (module-mid elem) mid) (return elem)))))
	(values
	 #'(lambda ()
	     "clear the database"
	     (setf roll () modules () high-sid 0 high-mid 0))
	 #'(lambda ()
	     "update student roll"
	    (do ((newname (prompt-for-student)(prompt-for-student))) ((string= newname ""))
	      (push (make-student :name newname :sid (incf high-sid)) roll)
	      (setf roll (sort roll #'string< :key #'student-name))))
	 #'(lambda ()
	     "update modules list"
	    (do ((newtitle (prompt-for-module)(prompt-for-module))) ((string= newtitle ""))
	      (push (make-module :title newtitle :mid (incf high-mid)) modules)
	      (setf modules (sort modules #'string< :key #'module-title))))
	 #'(lambda (sid mid)
	     "Enrol a student on a course module"
	     (push mid (student-modules (find-student sid)))
	     (push sid (module-students (find-module mid))))
	 #'find-student
	 #'find-module
	 #'(lambda ()
	     "Print students"
	     (dolist (elem roll) (print elem)))
	 #'(lambda ()
	     "Print modules"
	     (dolist (elem modules) (print elem)))))))


This works perfectly in cmucl, but lispworks produces compilation errors
viz:


The following functions are undefined:
STUDENT-MODULES which is referenced by MAKE-STUDENT-DB
MAKE-STUDENT which is referenced by MAKE-STUDENT-DB
MODULE-STUDENTS which is referenced by MAKE-STUDENT-DB
STUDENT-SID which is referenced by MAKE-STUDENT-DB
MAKE-MODULE which is referenced by MAKE-STUDENT-DB
(SETF STUDENT-MODULES) which is referenced by MAKE-STUDENT-DB
MODULE-MID which is referenced by MAKE-STUDENT-DB
(SETF MODULE-STUDENTS) which is referenced by MAKE-STUDENT-DB

; Loading fasl file /home/will/zx/dev/temp/make-student-db.ufsl
;  Loading fasl file /home/will/lwl/lib/4-1-0-0/modules/concat/xref.ufsl

It doesn't seem to like the default accessor names, however, it produces an
output file and then mostly works -

CL-USER 3 : 1 > (multiple-value-setq (clear update-students update-modules enrol find-student find-module print-students print-modules)(make-student-db))
#<closure (SUBFUNCTION 1 MAKE-STUDENT-DB) 212B073A>

CL-USER 4 : 1 > (funcall clear)
0

......


CL-USER 7 : 1 > (funcall print-students)

#S(STUDENT NAME "fred" SID 3 MODULES NIL) 
#S(STUDENT NAME "rose" SID 2 MODULES NIL) 
#S(STUDENT NAME "will" SID 1 MODULES NIL) 
NIL

CL-USER 8 : 1 > (funcall print-modules)

#S(MODULE TITLE "cornflakes" MID 4 STUDENTS NIL) 
#S(MODULE TITLE "english" MID 3 STUDENTS NIL) 
#S(MODULE TITLE "french" MID 2 STUDENTS NIL) 
#S(MODULE TITLE "history" MID 1 STUDENTS NIL) 
NIL


but

CL-USER 11 : 1 > (funcall enrol 1 1)

Error: Undefined function (SETF STUDENT-MODULES) called with arguments ((1) #S(STUDENT NAME "will" SID 1 MODULES NIL)).
  

Can anyone explain what I have got wrong, and why the compilers differ. 

Thanks in advance

From: JP Massar
Subject: Re: exercise problem
Date: 
Message-ID: <3f3af19e.186943378@netnews.comcast.net>
On Wed, 13 Aug 2003 17:59:10 +0100, Will White
<··········@ntlworld.com> wrote:

>
>hi - I am trying to learn cl. This is what I did for an exercise in Nick Levine's
>lecture notes (the end of week 7, as pointed to in somewhere like alu):
>
>(defun make-student-db ()
>  ;; store a crude database of students and modules in a closure
>  ;; it is accessed by functions returned by values, to be used by e.g.
>  ;; multiple-value-setq *clear* *update-students* .. etc
>  (defstruct student
>    name
>    SID        ; an integer
>    modules)
>  (defstruct module
>    title
>    mid
>    students)
 
>
>
>The following functions are undefined:
>STUDENT-MODULES which is referenced by MAKE-STUDENT-DB
>MAKE-STUDENT which is referenced by MAKE-STUDENT-DB
>MODULE-STUDENTS which is referenced by MAKE-STUDENT-DB
>STUDENT-SID which is referenced by MAKE-STUDENT-DB
>MAKE-MODULE which is referenced by MAKE-STUDENT-DB
>(SETF STUDENT-MODULES) which is referenced by MAKE-STUDENT-DB
>MODULE-MID which is referenced by MAKE-STUDENT-DB
>(SETF MODULE-STUDENTS) which is referenced by MAKE-STUDENT-DB
>
 
>  
>
>Can anyone explain what I have got wrong, and why the compilers differ. 
>
 
I suspect that your problem lies in having your DEFSTRUCT definitions
inside the DEFUN.

DEFSTRUCT definitions are global; putting them inside the DEFUN isn't
going to make them local to that function.

Take them out of the DEFUN and see if it all works.
From: Nick Levine
Subject: Re: exercise problem
Date: 
Message-ID: <8732fc48.0308140041.5d6badea@posting.google.com>
Let's reduce this to a simpler form:

(compile (defun foo ()
           (defstruct foo bar)
           (setf (foo-bar (make-foo)) nil)))

-> (LispWorks) "The following function is undefined: (SETF FOO-BAR)
which is referenced by FOO"

The spec says <http://www.lispworks.com/reference/HyperSpec/Body/m_defstr.htm>:

  "If a defstruct form appears as a top level form, the compiler must
  ... make the structure slot readers known to setf."

However, the defstuct here is not at top-level. Section 3.2.3.1.1
(Processing of Defining Macros)
<http://www.lispworks.com/reference/HyperSpec/Body/03_bcaa.htm> says:

  "... these compile-time side effects happen only when the defining macros
  appear at top level."

So it might actually be the case that cmucl is wrong, and Lispworks is
correct in not performing the side-effect of making the structure slot
reader for FOO-BAR known to setf.

(Erm, have I got this right?)

- nick
From: Christophe Rhodes
Subject: Re: exercise problem
Date: 
Message-ID: <sqvft0pm9w.fsf@lambda.jcn.srcf.net>
···@ravenbrook.com (Nick Levine) writes:

> Let's reduce this to a simpler form:
>
> (compile (defun foo ()
>            (defstruct foo bar)
>            (setf (foo-bar (make-foo)) nil)))
>
> -> (LispWorks) "The following function is undefined: (SETF FOO-BAR)
> which is referenced by FOO"
> [...]
> (Erm, have I got this right?)

I think correctness of implementations' behaviour depends on how they
implement structure slot setters.  CMUCL's DEFSTRUCT not at top level
is demonstrably non-conforming, as can be seen by the following:

(defun foo ()
  (if *some-boolean*
      (defstruct foo a)
      (defstruct foo a b)))

just compiling that definition in CMUCL will cause all sorts of
errors.  LispWorks is certainly justified in warning that the (SETF
FOO-BAR) function is undefined when it compiles FOO, because according
to ANSI the (SETF FOO-BAR) function need not be made known to the
compiler.  Whether the FOO function then goes on to work when executed
depends on whether the implementation provides (setf foo-bar)
functions or a setf macro; if the latter, then FOO will cause an
undefined function error at runtime.

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)