I always liked Erik Naggum's name for your WHEN-BIND: WHEREAS
http://groups.google.com/group/comp.lang.lisp/msg/bc7772aa5ab1f3e4
-Peter
On Tue, Sep 28, 2010 at 3:04 PM, Daniel Weinreb dlw@itasoftware.com wrote:
Attila Lendvai wrote:
macros that are useful and should be allowed. It is just that anaphoric macros, despite all their cuteness, tax code readers unnecessarily hard and thus have not been adopted as acceptable practice in multi-programmer environments.
well, it's one opinion.
there's at least one counter example in our team of 4. and i personally do like reasonable usage of anaphoric macros, even when done by my colleagues. and the reason is exactly that it *rises* code readability for us.
our practice includes special coloring for "it" and avoids any usage which is not blatantly trivial.
OK, if you're going to do that, that helps a lot.
A problem with this is that code that starts blatently trivial can grow. This is one reason that you see enormous Perl scripts; people thought they'd be simple enough that Perl was appropriate, but they grow over time.
Suddenly you have an outer "it" and an inner "it", and you need to "let" the outer one if you need to access that quantity in the inner one. And someone reading the code can get confuse about which one you mean.
Instead of "awhen", what we use here is called when-bind. Example (real one, from actual airline code):
(or (when-bind (dmc (message-dmc (request-task-message rt))) (dmc-struct-maximum-number-of-fdrs-in-response dmc)) (iatci-config :max-response-fdrs rt nil))
You specify the name. It's not anamorphic. The name can be meaningful, rather than "it". There are no conflicts. It does not get ugly when the code gets larger.
The downside is that it's more verbose, because you have to put in the name of the variable, and there is one more level of paren to keep things Lispy.
By the way, there is a sort of anamorphic thing in the "Clojure" dialect, where you can write little lambdas without arglists, by using $1 and $2 and so on to mean first arg, second arg, and so on. Again, he's trying to make programming with functions more friendly by making it more succinct, and I'm sure he'd also say "you should only use it for simple cases".
If you are following good practices, you'd stop using these things when the code gets large. Having automatic refactoring IDE's that could do that would be one way of trying to get the best of both words.
On the other hand, we do have an anmorphic macro that we sometimes use, in the sense that it makes up its own symbols. It is called define-class, and tries to be more like the Flavors class definer. The CLOS one, I think in an attempt to entirely avoid anamorphism, makes you spell out the names of the accessors (and/or readers and writers). define-class creates these names.
(define-class puma-request-context (migration-request-context) ((journey :type qapi-journey) (pnr :type qapi-pnr) (staged-pnr :type staged-puma-pnr) (pax-map :type list) ; a list containing elements of form (staged-pax-name . qapi-pnr-passenger) (seg-map :type list) ; a list containing elements of form (staged-segment . qapi-pnr-segment) (slices :type list) ; a list of qapi-pnr-slice (staging-to-action-map ;; A list of two element sublists associating a staging object with a QAPI action. :type list :initform nil)))
expands into
(PROGN (DEFCLASS PUMA-REQUEST-CONTEXT (MIGRATION-REQUEST-CONTEXT) ((JOURNEY :ACCESSOR PUMA-REQUEST-CONTEXT-JOURNEY :INITARG :JOURNEY :TYPE QAPI-JOURNEY) (PNR :ACCESSOR PUMA-REQUEST-CONTEXT-PNR :INITARG :PNR :TYPE QAPI-PNR) (STAGED-PNR :ACCESSOR PUMA-REQUEST-CONTEXT-STAGED-PNR :INITARG :STAGED-PNR :TYPE STAGED-PUMA-PNR) (PAX-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-PAX-MAP :INITARG :PAX-MAP :TYPE LIST) (SEG-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-SEG-MAP :INITARG :SEG-MAP :TYPE LIST) (SLICES :ACCESSOR PUMA-REQUEST-CONTEXT-SLICES :INITARG :SLICES :TYPE LIST) (STAGING-TO-ACTION-MAP :ACCESSOR PUMA-REQUEST-CONTEXT-STAGING-TO-ACTION-MAP :INITARG :STAGING-TO-ACTION-MAP :TYPE LIST :INITFORM NIL))) (DECLARE-LIST-OF PUMA-REQUEST-CONTEXT) (DEFUN MAKE-PUMA-REQUEST-CONTEXT (&REST QUUX::KEYS &KEY JOURNEY PNR STAGED-PNR PAX-MAP SEG-MAP SLICES STAGING-TO-ACTION-MAP &ALLOW-OTHER-KEYS) (DECLARE (IGNORABLE JOURNEY PNR STAGED-PNR PAX-MAP SEG-MAP SLICES STAGING-TO-ACTION-MAP)) (DECLARE (DYNAMIC-EXTENT QUUX::KEYS)) (APPLY #'MAKE-INSTANCE 'PUMA-REQUEST-CONTEXT QUUX::KEYS)) (DEFINE-COMPILER-MACRO MAKE-PUMA-REQUEST-CONTEXT (&REST QUUX::ARGS) (LIST* 'MAKE-INSTANCE (LIST* (LIST* 'QUOTE (LIST 'PUMA-REQUEST-CONTEXT)) QUUX::ARGS))) (FIND-CLASS 'PUMA-REQUEST-CONTEXT))
Some of our code uses this and some does not, at the whim of the programmer (perhaps that's poor practice). However, I haven't noticed this causing any problems.
pro mailing list pro@common-lisp.net http://common-lisp.net/cgi-bin/mailman/listinfo/pro