From: Gabor Melis
Subject: Anaphoric macros and packages
Date: 
Message-ID: <fb0fb805.0212040302.1f3895f@posting.google.com>
In Paul Graham's On Lisp, there is a chapter dedicated to anaphoric
macros. One of the simplest ones is 'aif' that binds 'it' to the value
of the condition evaluated in an 'if':

(defmacro aif (test-form then-form &optional else-form)
  `(let ((it ,test-form))
     (if it ,then-form ,else-form)))

;;; example
(aif 5
  (print it))

Now, it works fine if the macro definition and expansion are done in
the same package, but falls on its face when not:

;;; package 1 with the macro def
(cl:defpackage "P1"
  (:use "COMMON-LISP")
  (:export "AIF"))

(in-package "P1")

(defmacro aif (test-form then-form &optional else-form)
  `(let ((it ,test-form))
     (if it ,then-form ,else-form)))

;;; package 2 that uses the macro
(cl:defpackage "P2"
  (:use "COMMON-LISP")
  (:use "P1"))

(in-package "P2")

;;; example
(aif 5
  (print it))

Loading this file in clisp (v2.27) results in:
*** - EVAL: variable IT has no value
and macroexpand-1 says that (aif 5 (print it)) is expanded to:
(LET ((P1::IT 5)) (IF P1::IT (PRINT IT) NIL))

P1::IT???
If I do a 'compile-defun-lisp' by hand in emacs for all toplevel
s-exps it works.

What did I do wrong?

Cheers, Gabor

From: Frode Vatvedt Fjeld
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <2h8yz6117y.fsf@vserver.cs.uit.no>
····@hotpop.com (Gabor Melis) writes:

> What did I do wrong?

Forget to export the symbol "it" from P1?

-- 
Frode Vatvedt Fjeld
From: Kenny Tilton
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <3DEEF1AF.2040908@nyc.rr.com>
Frode Vatvedt Fjeld wrote:
> ····@hotpop.com (Gabor Melis) writes:
> 
> 
>>What did I do wrong?
> 
> 
> Forget to export the symbol "it" from P1?
> 

Jeez, could you be more terse? The guy's a newbie, maybe you could cut 
it down to two words: "Export 'it'?" Or were you just being pedantic?

:)

-- 

  kenny tilton
  clinisys, inc
  ---------------------------------------------------------------
""Well, I've wrestled with reality for thirty-five years, Doctor,
   and I'm happy to state I finally won out over it.""
                                                   Elwood P. Dowd
From: Frode Vatvedt Fjeld
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <2hfztczv4g.fsf@vserver.cs.uit.no>
Kenny Tilton <·······@nyc.rr.com> writes:

> Jeez, could you be more terse? The guy's a newbie, maybe you could
> cut it down to two words: "Export 'it'?" Or were you just being
> pedantic?

I'm just a believer in letting people making their own mistakes, I
guess. I think the aif thingy is a big mistake, and the OP's trouble
with packages was just derailing him from a speedy arrival at the same
conclusion..

-- 
Frode Vatvedt Fjeld
From: Arthur Lemmens
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <3DEDE5E4.A9A8EA97@xs4all.nl>
Gabor Melis wrote:
> 
> In Paul Graham's On Lisp, there is a chapter dedicated to anaphoric
> macros. One of the simplest ones is 'aif' that binds 'it' to the value
> of the condition evaluated in an 'if':
> 
> (defmacro aif (test-form then-form &optional else-form)
>   `(let ((it ,test-form))
>      (if it ,then-form ,else-form)))

> Now, it works fine if the macro definition and expansion are done in
> the same package, but falls on its face when not:

Yes. That's one reason why some people (me included) prefer something like
WHEN-LET:

  (defmacro when-let ((var test-form) &body body)
    `(let ((,var ,test-form))
       (when ,var ,@body)))

  ;; Example:
  (when-let (it (hairy-computation))
    (print it))

> What did I do wrong?

Nothing, really. But I think that Paul Graham did something wrong with 
the design of AIF.

Arthur Lemmens
From: Gabor Melis
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <fb0fb805.0212040945.eb8ca48@posting.google.com>
Arthur Lemmens <········@xs4all.nl> wrote in message news:<·················@xs4all.nl>...
> Gabor Melis wrote:
> > What did I do wrong?
> 
> Nothing, really. But I think that Paul Graham did something wrong with 
> the design of AIF.

OK. I am feeling better, but I need to know why it doesn't work. It
looks right. And it works right if I do the compile-defun-lisp trick
in the original post.
From: Simon Andr�s
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <vcdel8x4low.fsf@tarski.math.bme.hu>
····@hotpop.com (Gabor Melis) writes:


> OK. I am feeling better, but I need to know why it doesn't work. It

Have you tried following Frode's advice and export the symbol IT
along with AIF from the P1 package? The double colon in the
macroexpansion is telling. 

Andras
From: Kenny Tilton
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <3DEEF0E9.4080705@nyc.rr.com>
Gabor Melis wrote:
> Arthur Lemmens <········@xs4all.nl> wrote in message news:<·················@xs4all.nl>...
> 
>>Gabor Melis wrote:
>>
>>>What did I do wrong?
>>
>>Nothing, really. But I think that Paul Graham did something wrong with 
>>the design of AIF.
> 
> 
> OK. I am feeling better, but I need to know why it doesn't work. It
> looks right. And it works right if I do the compile-defun-lisp trick
> in the original post.

Slow down. It does /not/ look right.[1] Graham did not have different 
packages. You added different packages. When tou change shit and shit 
breaks, dwell first on the changes you made. Packages.

Did you really want to master packages and macros at /the same time/? 
Brave soul! :)

If so, its simple: symbols "coded" by a macro, while reasonably viewed 
to be expanded into the code where the macro is coded, do /not/ in fact 
invade the namespace of a /different/ package of the code referencing 
the macro, as would code actually written there.

So by default "it" does not invade the namespace of package P2. Even if 
P2 "uses" P1. But! If P1 exports "it", the body of code provided to AIF 
will "see" the "it" provided by p1.

[1] I have no idea why an emacs compile-defun-lisp
  "trick" makes it work, i don't happen to do emacs. i do know truly
interpreted shit works where compiled doesn't (why I do not know) so
maybe you are in this area.

-- 

  kenny tilton
  clinisys, inc
  ---------------------------------------------------------------
""Well, I've wrestled with reality for thirty-five years, Doctor,
   and I'm happy to state I finally won out over it.""
                                                   Elwood P. Dowd
From: Rob Warnock
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <xamcnVCNJ8ZEoXKgXTWcoQ@giganews.com>
Arthur Lemmens  <········@xs4all.nl> wrote:
+---------------
| But I think that Paul Graham did something wrong with the design of AIF.
+---------------

Note that ANSI CL itself already contains some anaphora:

	(let ((list '(nil (a b) nil (c d e) nil (f g h i) (j k l))))
	  (loop for i in list
	    when (third i)	; pun intended.
	     collect it))
	=> (E H L)


-Rob

-----
Rob Warnock, PP-ASEL-IA		<····@rpw3.org>
627 26th Avenue			<URL:http://www.rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Frode Vatvedt Fjeld
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <2h7keoznpq.fsf@vserver.cs.uit.no>
····@rpw3.org (Rob Warnock) writes:

> Note that ANSI CL itself already contains some anaphora:
>
> 	(let ((list '(nil (a b) nil (c d e) nil (f g h i) (j k l))))
> 	  (loop for i in list
> 	    when (third i)	; pun intended.
> 	     collect it))
> 	=> (E H L)

I don't think the "it" here is much more magical than the other loop
keywords. If you try "collect (identity it)", it won't work.

-- 
Frode Vatvedt Fjeld
From: Michael Hudson
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <7h34r9sldz2.fsf@pc150.maths.bris.ac.uk>
Frode Vatvedt Fjeld <······@cs.uit.no> writes:

> I don't think the "it" here is much more magical than the other loop
> keywords.

And, somewhat related to this thread, loop keywords are recognized by
symbol name and not identity.  I wonder if the first implementation of
loop did that?

This is what makes it possible to write them as keywords (in another
sense of the word...), something that makes them stand out nicely
(with font-lock, anyway)..

Cheers,
M.

-- 
  Richard Gabriel was wrong: worse is not better, lying is better.
  Languages and systems succeed in the marketplace to the extent that
  their proponents lie about what they can do.
                                       -- Tim Bradshaw, comp.lang.lisp
From: Erik Naggum
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <3248033830373991@naggum.no>
* Gabor Melis
| What did I do wrong?

  You used a poorly designed feature.

  The key here is to get a short-hand for

(let ((expr (whatever)))
  (when expr
    (something-using-expr)))

  but in the tradition of really badly designed languages, an implicit
  binding is used instead of doing explicit bindings in the Common Lisp
  tradition, which would also allow more than one binding/expression.

  I would much prefer a syntax like this:

(whereas ((expr-1 (whatever-1))
          (expr-2 (whatever-2)))
  (something-using-expr-1-and/or-expr-2))

  Reading it like the standard contract legalese, the key idea is that all
  prior forms are true when an expression in the binding forms is evaluated
  (just like the operator `and�) and prior variable are bound to the (true)
  value of each prior expression.  The body is an implicit `progn� that can
  thus rely on all of these forms having non-nil values.

  Having exhausted all useful things to do, I, too, have been thinking about
  how to do bindings and declarations more conveniently, and, not being the
  least bit afraid of parentheses, have decided on binding forms that have
  the general structure

(var-list [expression [decl-list]])

  where var-list is a designator for a list of (symbols naming) variables,
  expression may return multiple values bound to those variables with the
  standard nil default, and decl-list is a designator for a list of types.
  To be gratuitously super-clever, if the type of a variable in `whereas�
  is an explicit union of `null� and something else to signal that `nil� is
  a valid value, the result of the test is that using the succeeding value
  (etc by induction) and if it is the last value, the entire form is used
  for bindings, only.  Some examples may yet have a slightly illuminating
  effect:

(whereas (((value present-p) (gethash ...) ((or null ...))))
  ;; value is now actually obtained from the hashtable
  ...)
= (multiple-value-bind (value present-p) (gethash ...)
     (declare (type (or null ...) value))
     (when present-p
       ...)))

(whereas ((index (position ...) fixnum))
  ;; index now holds a fixnum index of an element actually in the sequence
  ...)
= (let ((index (position ...)))
    (when index
      (locally (declare (fixnum index))
        ...)))

(whereas ((cell (assoc ...) cons)
          (value (cdr cell)))
  ;; cell now holds an actually matching cons cell from the alist
  )
= (let ((cell (assoc ...)))
    (when cell
      (locally (declare (cons cell))
        (let ((value (cdr cell)))
          (when value
            ...)))))

  My `let� and other binding forms parallel this development with a final
  (additional) argument that is the declared types, and allow a list of
  variables for binding multiple values.  Required arguments in lambda
  lists may be typed just like methods on generic functions.  The amount of
  effort to get this done is surprisingly small, and making it fully
  backward-compatible is almost easier than breaking things.

  Please remember that the purpose of this stunt is to give people a good
  reason to stay away from ill-designed and very un-Common-Lispy ways of
  "improving" on the language.  Macros that bind things and which neither
  work when nested nor when na�vely exported in the package system may
  appear "cool" to people who come from other languages, but not to the
  seasoned users.  Just like some may believe that "then" and "else" have a
  place in `if� "statements" because the language they /really/ want to use
  does that, misguided attempts annoy people more than benefit a community.
  
-- 
Erik Naggum, Oslo, Norway

Act from reason, and failure makes you rethink and study harder.
Act from faith, and failure makes you blame someone and push harder.
From: Thomas F. Burdick
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <xcvlm35djga.fsf@earthquake.OCF.Berkeley.EDU>
Erik Naggum <····@naggum.no> writes:

>   I would much prefer a syntax like this:
> 
> (whereas ((expr-1 (whatever-1))
>           (expr-2 (whatever-2)))
>   (something-using-expr-1-and/or-expr-2))

BTW, Erik, this is fantastic.  I had was only somewhat satisfied with
my if-let macro (if-let (var expression) then [else]), and in fact, I
never use it.  I do use my binding-cond macro sometimes
(binding-cond var (expr form...) ...).  Yours is a cute name, but I
especially like the idea of having a full sequence of let forms
available.

>   Having exhausted all useful things to do, I, too, have been thinking about
>   how to do bindings and declarations more conveniently, and, not being the
>   least bit afraid of parentheses, have decided on binding forms that have
>   the general structure
> 
> (var-list [expression [decl-list]])

Hey, great minds I guess (or bored or idle minds, maybe).  I have been
using:

  (vars-form [expression [&rest declarations]])
  vars-form ::= var | var-list
  var-list ::= (var-name ...)
  var-name ::= var | (var type)

Which is a little more complicated to implement, but pretty much the
same idea.  It makes type declarations easier than others, because I
use them far mor often than any other type of declaration.  I think
this form is even less for those afraid of parens, though:

  (tfb:let. ((((x integer) (y some-type)) (compute-values))
             (((z number)) (compute-more)))
    (foo x y z))

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: JP Massar
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <3deee20c.166155289@netnews.attbi.com>
On 4 Dec 2002 03:02:34 -0800, ····@hotpop.com (Gabor Melis) wrote:
 
>;;; package 1 with the macro def
>(cl:defpackage "P1"
>  (:use "COMMON-LISP")
>  (:export "AIF"))
>
>(in-package "P1")
>
>(defmacro aif (test-form then-form &optional else-form)
>  `(let ((it ,test-form))
>     (if it ,then-form ,else-form)))
>
>;;; package 2 that uses the macro
>(cl:defpackage "P2"
>  (:use "COMMON-LISP")
>  (:use "P1"))
>
>(in-package "P2")
>
>;;; example
>(aif 5
>  (print it))
>
>Loading this file in clisp (v2.27) results in:
>*** - EVAL: variable IT has no value
>and macroexpand-1 says that (aif 5 (print it)) is expanded to:
>(LET ((P1::IT 5)) (IF P1::IT (PRINT IT) NIL))
>
>P1::IT???

The fact that you question why it is P1::IT instead of
(what?  P2::IT ?) says to me you need to understand about
how the Lisp Reader works, how INTERN works and how it
interacts with the Lisp Reader, and perhaps how macro expansion
works.

Briefly, the 'IT' is seen by the reader and turned into a
symbol in the P1 package by the reader (because you did an
'in-package' to P1).  The defmacro 'really' looks like

(lisp::defmacro p1::aif (...)
  `(lisp::et ((p1::it ,p1::test-form))
   ...
   )

after it is read in.  

The 'IT' in (aif 5 (print it)) after you do (in-package "P2")
is likewise really p2::it, and these two symbols have
absolutely nothing to do with one another.

Does this help?




>If I do a 'compile-defun-lisp' by hand in emacs for all toplevel
>s-exps it works.
>
 
Not sure why this is working for you.  I don't think it should.
From: Gabor Melis
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <fb0fb805.0212050637.48ad377f@posting.google.com>
······@alum.mit.edu (JP Massar) wrote in message news:<··················@netnews.attbi.com>...
> The fact that you question why it is P1::IT instead of
> (what?  P2::IT ?) says to me you need to understand about
> how the Lisp Reader works, how INTERN works and how it
> interacts with the Lisp Reader, and perhaps how macro expansion
> works.
> 
> Briefly, the 'IT' is seen by the reader and turned into a
> symbol in the P1 package by the reader (because you did an
> 'in-package' to P1).  The defmacro 'really' looks like
> 
> (lisp::defmacro p1::aif (...)
>   `(lisp::et ((p1::it ,p1::test-form))
>    ...
>    )
> 
> after it is read in.  
> 
> The 'IT' in (aif 5 (print it)) after you do (in-package "P2")
> is likewise really p2::it, and these two symbols have
> absolutely nothing to do with one another.
> 
> Does this help?

Yes. And the other replies helped, too. Even though the spec does not
imply it, for some reason I had the mental picture of the result of
the macro expansion being fed to the reader. Now, it seems clear how
it (doesn't) work(s) and why exporting IT helped.

And as a side-effect (should be avoided, I know :-)), anaphoric macros
lost some of their allure.

Thanks, Gabor
From: Hannah Schroeter
Subject: Re: Anaphoric macros and packages
Date: 
Message-ID: <aso5ij$9d$1@c3po.schlund.de>
Hello!

Gabor Melis <····@hotpop.com> wrote:
>[...]

>And as a side-effect (should be avoided, I know :-)), anaphoric macros
>lost some of their allure.

We're not so dogmatic wrt side effects on this side of the great
Scheme/CL divide. :-)

Kind regards,

Hannah.