[pro] The Best Examples of "Code is Data"

Daniel Weinreb dlw at itasoftware.com
Tue Sep 28 22:04:52 UTC 2010



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.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/pro/attachments/20100928/08fecddf/attachment.html>


More information about the pro mailing list