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':
;;; 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.
> 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:
> 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.
m...@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.
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.
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.
>;;; 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
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.
>>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
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
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! | / / `-----------------------' ( -. | | ) | (`-. '--.) `. )----'
Kenny Tilton <ktil...@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..
Arthur Lemmens <alemm...@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 <r...@rpw3.org> 627 26th Avenue <URL:http://www.rpw3.org/> San Mateo, CA 94403 (650)572-2607
mas...@alum.mit.edu (JP Massar) wrote in message <news:3deee20c.166155289@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
> 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.
> 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