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.