From: Kenny Tilton
Subject: Re: For Kenny Tilton: Why do I need macros revisited.
Date: 
Message-ID: <3F466BD9.1080704@nyc.rr.com>
Andrew Dalke wrote:
> Kenny Tilton:
> 
>>We have pointed out again and again
>>that macros are no harder to understand than functions or classes, but
>>no one addresses that point.
> 
> 
> What about Terry's comment, based on the above URL:
> ) Reading the third referenced page on Macros, I notice that the amount
> ) of syntax definition for the macro sublanguage is as large as a
> ) substantial portion (one-third?) of that for core Python (if condensed
> ) to the same density). So, just by definitional bulk, having it in the
> ) language would not be a free ride.

Oy. This comment is about understanding how to write macros. We are 
talking about whether:

     (with-open-file (f "my.data" <...options...>)
        (do-fun-things-with f))

...is harder to understand than:

     (open-file-and-do "my.data"
           <...options...>
           :do-this (lambda (f) (do-fun-things-with f)))

btw, /writing/ macros takes a while to learn, or it did for me anyway, 
because one has moved to a different space than a programmer normally 
occupies, somewhere inside the compiler you might say. So some of the 
time you are typing in code for run-time and some of the time you are 
typing in code that runs at compile time to produce the code that runs 
at runtime. And there's some weird syntax, with backquotes and coomas 
and ampersands. Great fun, once it is second nature, but till then...the 
neat thing is realizing you can put in debug statements that execute 
while the macro is being expanded, not at run time. And you can have a 
bug in the expanding code such that evaluating the form dies before it 
begins being evaluated! <g>

> 
> 
>>Strangely, quite a few of you have also conceded macros can leverage a
>>language.
> 
> 
> Leverage a language ... for what?  And for whom? 

The keystone macro of the RoboCup client I plan to share with soccer 
hooligans everywhere is DEFTASK:

(deftask <taskname> &key achieved subtasks wait-on-subtasks?
                          attempt retry-when)

achieved is a rule for deciding when the task has been achieved.

subtasks are subtasks to be completed (achieved) before the task at hand 
is attempted, unless...

wait-on-subtasks? is nil, in which case the task can decide on its won 
when to check if it has alreadt been achieved (you'll need an example).

attempt is code which generates a command to the soccer server, 
hopefully leading to task achievement

retry-when tells the engine when to try again if not yet achieved. eg, 
don't turn again to look for the ball until we have gotten a new "see" 
message.

Now that's pretty slim doc, but there will be more and examples galore 
(and an on-line help desk <g>), but some points:

No, no one looking at my code has ever seen deftask before. But if they 
have done any lisp at all, they will know I have built a little task 
solving engine for them. They will know because I used standard Lisp 
terminology, the "def" prefix. They will understand all the keywords, 
but immediately scream for documentation because keywords do not a 
user's guide make. They will also see that the style is declarative, 
which is meant to be helpful (and is) but they will sweat bullets until 
they have a solid feel for how the engine works inside ("why did a 
completed subtask start getting solved again"). I'll have to come clean 
on the guts of the engine.

Or, as some of the folks on our team have done, they can macroexpand any 
deftask form. They can look at the source of deftask as well, but that 
can be harder because the final output is kinda disguised by all the 
code which builds the final output. Once they see the astonishing 
output, they will likely decide just to have me provide a functional 
spec so they do not have to worry about the engine internals. Like I do.

They just write:

(deftask head-for-ball () ;; literally set body heading for ball
   ()
   :achieved (bwhen (ball (^ball))
               (< (+ .neck. (seen-dir ball)) 10))
   :subtasks (list (mktask find-ball))
   :attempt (bwhen (ball (^ball))
              (let ((correction (+ .neck. (seen-dir ball))))
                (ptrc nil "correcting" correction) ;;debug macro!
                `(turn ,correction))))

The unadvertised synatx above (the two () forms near the beginning, are 
a giveaway that deftask expands into the familiar (to lispniks) defclass 
form. ie, deftask is implemented as defclass. Again, I make an effort to 
keep the syntax lispy and recognizable and familiar and all that good 
stuff. They can add slots to their task, have supertasks, etc etc.

So the defclass power is /leveraged/ to make deftask even better than i 
could make it. Generic functions automatically can be dispatched by 
soccer task, without me lifting a finger.

In the end, a bunch of people with just an interest in machine learning 
and genetic algorithms and soccer can have some fun concnetrating just 
on the  ML and GA and football. Because I am providing a task-executing 
"language" which deals with the soccer server for them.

As for what the above would look like without macros:

(defmodel head-for-ball (task)
   ()
   (:default-initargs
       :wait-on-subtasks-p t
     :kids (c? (let ((task self) (self (player self)))
                 (mapcar (lambda (subtask)
                           (if (symbolp subtask)
                               (make-instance subtask :player self)
                             subtask))
                   (thekids (list (mktask find-ball))))))
     :achieved-body  (lambda (cells::c &aux (task (cells::c-model 
cells::c)) (self (player task)))
                       (declare (ignorable self task))
                       (bwhen (ball (^ball))
                         (< (+ .neck. (seen-dir ball)) 10)))
     :retry-when-body (lambda (cells::c &aux (task (cells::c-model 
cells::c)) (self (player task)))
                        (declare (ignorable cells::c self task))
                        nil)
     :attempt-body (lambda (cells::c &aux (task (cells::c-model 
cells::c)) (self (player task)))
                     (declare (ignorable self task))
                     (bwhen (ball (^ball))
                       (let ((correction (+ .neck. (seen-dir ball))))
                         (ptrc nil "head-for-ball correcting" correction)
                         `(turn ,correction))))))

Not too bad, but then you have to expand defmodel. :)

-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Career highlights? I had two. I got an intentional walk from
Sandy Koufax and I got out of a rundown against the Mets."
                                                  -- Bob Uecker
From: Joe Marshall
Subject: Re: For Kenny Tilton: Why do I need macros revisited.
Date: 
Message-ID: <fzjth10q.fsf@ccs.neu.edu>
Kenny Tilton <·······@nyc.rr.com> writes:

> Oy. This comment is about understanding how to write macros. We are
> talking about whether:
>
>      (with-open-file (f "my.data" <...options...>)
>         (do-fun-things-with f))
>
> ...is harder to understand than:
>
>      (open-file-and-do "my.data" <...options...>
>            :do-this (lambda (f) (do-fun-things-with f)))

Since with-open-file is already supplied in lisp, and since
re-inventing the wheel with different syntax is bad, let me
rephrase your question:

is this:

  (with-standard-foo (foo :color 'green :paper-or-plastic :plastic)
     (if (nearly-correct-p foo)
         (tweak foo)
         (twiddle foo)))

obviously better or worse than:

  (call-with-standard-foo
     :color 'green
     :paper-or-plastic :plastic
     :receiver (lambda (foo)
                 (if (nearly-correct-p foo)
                     (tweak foo)
                     (twiddle foo))))


While the latter form is less likely to be found in a
traditional lisp implementation, there is virtually no
difference in the set of characters typed.  The former version
may perform better on lisps that have a hard time analyzing
lambda expressions, but you could easily write a compiler-macro
for the latter that re-writes it to the former.

In the code I work with, you're just as likely to see either
or both with the `WITH-..' macro expanding into a call to the
`CALL-WITH-...' function.