From: ··············@gmail.com
Subject: Declared variable being treated as special in a macro call.
Date: 
Message-ID: <1169424512.951985.216180@38g2000cwa.googlegroups.com>
I have this function, that tests some sample data to make sure that
each subject in a fake examinations system has at least 3 examiners
assigned to it.

(defun test-sample-data-enough-examiners-per-subject ()
  (let ((db (make-sample-db)))
    (for-each-subject (subject db)
		      (lambda () (> (length (examiners-for-subject db subject))
3)))))

make-sample-db returns an 'exam-db' object, examiners-for-subject
returns a list of examiner objects.

for-each-subject is a macro I defined to loop over all the subjects in
the database.  Here it is:

(defmacro for-each-subject ((subject db) &body body)
  `(loop for ,subject in (subjects ,db) collect ,body))

I compile the code from within SLIME, and get this output:

WARNING in TEST-SAMPLE-DATA-ENOUGH-EXAMINERS-PER-SUBJECT-1 in lines
127..132 :
SUBJECT is neither declared nor bound,
it will be treated as if it were declared SPECIAL.

However, when I macroexpand-1 the for-each-subject call, I get this:

(LOOP FOR SUBJECT IN (SUBJECTS DB) COLLECT
 ((LAMBDA NIL (> (LENGTH (EXAMINERS-FOR-SUBJECT DB SUBJECT)) 3))))

I don't see the problem; subject appears to be declared there.

Also, I'm not sure why I seem to have to provide 'body' as a lambda
expression; just responding to the compiler's instructions.

Thanks for your help.

Ricky.

P.S., it looks like homework, mainly because I'm partly a lecturer; I
have some students implementing the same thing but in Java - I thought
I'd implement it in Lisp as a learning experience and so that I
anticipate any problems the students have.

From: Pascal Bourguignon
Subject: Re: Declared variable being treated as special in a macro call.
Date: 
Message-ID: <87ejpnq4p8.fsf@thalassa.informatimago.com>
···············@gmail.com" <··············@gmail.com> writes:
> [...]
> for-each-subject is a macro I defined to loop over all the subjects in
> the database.  Here it is:
>
> (defmacro for-each-subject ((subject db) &body body)
>   `(loop for ,subject in (subjects ,db) collect ,body))

:COLLECT takes ONE expression. &BODY binds to BODY a list of
expressions.  Therefore you need something like PROGN to make it work.

(defmacro for-each-subject ((subject db) &body body)
  `(loop :for ,subject :in (subjects ,db) :collect (progn ,@body)))

Use macroexpand-1 !

Try:

(macroexpand-1 '(for-each-subject (subj db) (print subj) (f subj)))

with both versions of the macro.

> I compile the code from within SLIME, and get this output:

What lisp implementation?

> WARNING in TEST-SAMPLE-DATA-ENOUGH-EXAMINERS-PER-SUBJECT-1 in lines
> 127..132 :
> SUBJECT is neither declared nor bound,
> it will be treated as if it were declared SPECIAL.
>
> However, when I macroexpand-1 the for-each-subject call, I get this:
>
> (LOOP FOR SUBJECT IN (SUBJECTS DB) COLLECT
>  ((LAMBDA NIL (> (LENGTH (EXAMINERS-FOR-SUBJECT DB SUBJECT)) 3))))
>
> I don't see the problem; subject appears to be declared there.

Not necessarily.  It looks like :FOR creates a variable named SUBJECT
only in the scope of a :DO, not of a :COLLECT.

Notice that CLHS specifies that LOOP may implement its loop variables
either by binding them once and modifying them in the loop, or by
binding them in the body of the loop everytime it loops. 


> Also, I'm not sure why I seem to have to provide 'body' as a lambda
> expression; just responding to the compiler's instructions.

I would write:

(loop :for subject :in (subjects db) 
      :collect (< 3 (length (examiners-for-subject db subject))))


For example, in sbcl:

S/CL-USER[13]> (loop :for subject :in '(1 2 3 4 5 6) :collect (< 3 subject))

(NIL NIL NIL T T T)






-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"This statement is false."            In Lisp: (defun Q () (eq nil (Q)))
From: ··············@gmail.com
Subject: Re: Declared variable being treated as special in a macro call.
Date: 
Message-ID: <1169427887.395758.162740@v45g2000cwv.googlegroups.com>
Pascal Bourguignon wrote:

> (defmacro for-each-subject ((subject db) &body body)
>   `(loop :for ,subject :in (subjects ,db) :collect (progn ,@body)))

That works nicely, and makes sense, thanks.

> Use macroexpand-1 !

I did, I expanded my macro and looked at the resulting source.

> Try:
>
> (macroexpand-1 '(for-each-subject (subj db) (print subj) (f subj)))
>
> with both versions of the macro.

I think I get it; before I was returning a list of results rather than
the last result from each body.

> What lisp implementation?

Clisp 2.37 as far as I can tell.  I suppose this output might be
useful:

CL-USER> (cl:lisp-implementation-version)
"2.37 (2006-01-02) (built on winsteingoldlap.bluelnk.net
[192.168.7.100])"
CL-USER> (cl:lisp-implementation-type)
"CLISP"

> Not necessarily.  It looks like :FOR creates a variable named SUBJECT
> only in the scope of a :DO, not of a :COLLECT.

But this code works, and gives no compile warnings:

(loop for i in '(1 2 3 4 5) collect (* i i))
From: Pascal Bourguignon
Subject: Re: Declared variable being treated as special in a macro call.
Date: 
Message-ID: <873b63p3l8.fsf@thalassa.informatimago.com>
···············@gmail.com" <··············@gmail.com> writes:

> Pascal Bourguignon wrote:
>
>> (defmacro for-each-subject ((subject db) &body body)
>>   `(loop :for ,subject :in (subjects ,db) :collect (progn ,@body)))
>
> That works nicely, and makes sense, thanks.
>
> [...]
>
>
>> Not necessarily.  It looks like :FOR creates a variable named SUBJECT
>> only in the scope of a :DO, not of a :COLLECT.
>
> But this code works, and gives no compile warnings:
>
> (loop for i in '(1 2 3 4 5) collect (* i i))

I think you got that warning about SUBJECT because you didn't define
the macro FOR-EACH-SUBJECT before you compiled that function.  When an
operator is unknown, it's assumed to be a function, not a macro.  So:

    (for-each-subject (subject db)
                      (lambda () (> (length (examiners-for-subject db subject)) 3)))

was interpreted as a call to the function FOR-EACH-SUBJECT with as
arguments, a call to the function SUBJECT with as argument DB, and the
anonymous function, which calls the function EXAMINERS-FOR-SUBJECT
with as arguments, DB and SUBJECT, and here is the first notice we
have of the variable SUBJECT.


There are two things:

- of course, you should let the lisp process know all the macros used
  by your expressions before you evaluate or compile them.

- in slime you should distinguish evaluation from compilation. SBCL
  users don't care, because their implementation compiles everything
  it evaluates (till they introduced an interpreter, but even then,
  you have to activate it manually AFAIK).  But when you are writting
  or debugging your programs with CLISP, you'll want to avoid
  compilation to get better debugging information.  So use C-x C-e
  instead of C-c C-c.

  Actually, I don't know if C-x C-e is bound in slime, I have the
  following in my ~/.emacs:

     (require 'slime)

     (defun pjb-slime-eval-last-expression ()
       "Evaluate the expression preceding point."
       (interactive)
       (if buffer-read-only
           (slime-eval-last-expression)
           (let ((str  (slime-last-expression)))
             (if (string-equal* "(DEF"  str :end2 4)
                 (slime-interactive-eval str)
                 (slime-eval-print-last-expression str)))))

     (define-key slime-mode-map  "\C-c\C-e" 'pjb-slime-eval-last-expression)
     (define-key slime-mode-map  "\C-x\C-e" 'pjb-slime-eval-last-expression)


  If you have made modifications in several parts of the source file,
  you can get them all taken into account at once with 
  M-x slime-eval-buffer RET.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

COMPONENT EQUIVALENCY NOTICE: The subatomic particles (electrons,
protons, etc.) comprising this product are exactly the same in every
measurable respect as those used in the products of other
manufacturers, and no claim to the contrary may legitimately be
expressed or implied.
From: ··············@gmail.com
Subject: Re: Declared variable being treated as special in a macro call.
Date: 
Message-ID: <1169480912.860347.229500@v45g2000cwv.googlegroups.com>
Pascal Bourguignon wrote:

> I think you got that warning about SUBJECT because you didn't define
> the macro FOR-EACH-SUBJECT before you compiled that function.

You're right - since posting I worked that out and moved the macros to
the top of the file.  Still, I understand a bit more now, and I don't
have to use lambda anymore with those macros.

>   Actually, I don't know if C-x C-e is bound in slime

It is, or at least it is in Lispbox.  Maybe Lispbox customises it a
lot.

>   If you have made modifications in several parts of the source file,
>   you can get them all taken into account at once with
>   M-x slime-eval-buffer RET.

I was going to the top of the file, pressing C-space, then the bottom,
and C-c C-r to achieve the same, so the command you just gave is much
appreciated.

Cheers,

Ricky.
From: Pascal Bourguignon
Subject: Re: Declared variable being treated as special in a macro call.
Date: 
Message-ID: <87d557nggo.fsf@thalassa.informatimago.com>
···············@gmail.com" <··············@gmail.com> writes:
>>   If you have made modifications in several parts of the source file,
>>   you can get them all taken into account at once with
>>   M-x slime-eval-buffer RET.
>
> I was going to the top of the file, pressing C-space, then the bottom,

C-x h  can be used to select (mark) the wHole buffer. 

> and C-c C-r to achieve the same, so the command you just gave is much
> appreciated.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

HEALTH WARNING: Care should be taken when lifting this product,
since its mass, and thus its weight, is dependent on its velocity
relative to the user.
From: Kaz Kylheku
Subject: Re: Declared variable being treated as special in a macro call.
Date: 
Message-ID: <1169509888.453626.285420@q2g2000cwa.googlegroups.com>
On Jan 21, 5:04 pm, ···············@gmail.com"
<··············@gmail.com> wrote:
> But this code works, and gives no compile warnings:
>
> (loop for i in '(1 2 3 4 5) collect (* i i))

In the code that produces the error, the reference to SUBJECTS is
wrapped in the form (EXAMINERS-FOR-SUBJECT DB SUBJECT).

One could easily contrive that form to produce the error, by for
instance making it a macro form which tries to EVAL its argument forms,
or generates code to arrange for that to happen.