Subject: Re: Anaphoric macros and packages From: Erik Naggum <erik@naggum.no> Date: 04 Dec 2002 23:37:10 +0000 Newsgroups: comp.lang.lisp 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.