A critic of Guido's blog on Python's lambda
Ken Tilton
kentilton at gmail.com
Tue May 16 12:30:02 EDT 2006
Ben wrote:
> This kind of discussions between two groups of people,
> neither of who know the other person's language very well just wind me
> up something chronic!
I must say, it is pretty funny how a flamewar turned into a pretty
interesting SoC project.
> Anything that makes programming more fun is good, and I hope the Lisp
> true way explodes into my head at some point.
Here is an excerpt of an excerpt from famous Gears demo. I notice it
really makes concrete what Lispniks are talking about in re macros and
multi-line lambda (pity about the formatting):
(defmodel gears-demo (window)
((gear-ct :initform (c-in 1) :accessor gear-ct :initarg :gear-ct))
(:default-initargs
:title$ "Rotating Gear Widget Test"
:kids (c? (the-kids
(mk-stack (:packing (c?pack-self))
(mk-row ()
(mk-button-ex (" Add " (incf (gear-ct .tkw))))
(mk-button-ex ("Remove" (when (plusp (gear-ct .tkw))
(decf (gear-ct .tkw)))))
(mk-entry :id :vtime
:md-value (c-in "10")))
(make-instance 'gears
:fm-parent *parent*
:width 400 :height 400
:timer-interval (max 1
(or (parse-integer (fm^v :vtime))
:junk-allowed t)
0)))))))
Don't worry, Lispniks cannot read that either. It is a delarative
construction of a hierarchical GUI. That is such a common task, that I
have rolled up a bunch of GUI-building macrology so that just the stuff
specific to this GUI gets typed in. Since the Gears widget is a custom
widget I have no macrology for that, and the wiring shows in the
expression ":fm-parent *parent*" (which itself leverages Lisp special
variables).
And no, I cannot remember all my macrology. I can certainly read it and
easily modify my GUI, because all the wiring is hidden, but if I have to
build a new GUI I cut and paste from other GUIs.
Let's look at just one form, which I believe destroys Alex's whole case
for naming every lambda:
(mk-button-ex ("Remove" (when (plusp (gear-ct .tkw))
(decf (gear-ct .tkw)))))
"mk-button-ex" (a) makes fun of MS$ naming standards and (b) expands to:
(make-instance 'button
:fm-parent *parent*
:text "remove"
:on-command (c? (lambda (self)
(when (plusp (gear-ct .tkw))
(decf (gear-ct .tkw))))))
The above is what one really needs to write to stick something in my GUI
framework, but who wants to look at all of that when most of it is
boilerplate? I need ":fm-parent *parent*" on every label and widget
because of some internals requirements, I just do not want to look at it
or have to remember to code it all the time (the latter not being a huge
problem because I really am cutting/pasting when I build a new GUI).
Is mk-button-ex some mysterious new language construct that will make
multi-programmer projects collapse in a heap of programmer-specific
constructs inscrutable to anyone else on the team?
(a) mk-button-ex kinda tells you (1) it makes a button and (2) no, this
is not part of Common Lisp, so where is the confusion?
(b) control-alt-. in my IDE shows me:
(defmacro mk-button-ex ((text command) &rest initargs)
`(make-instance 'button
:fm-parent *parent*
:text ,text
:on-command (c? (lambda (self)
(declare (ignorable self))
,command))
, at initargs))
Looks a lot like the expansion, right? That is really important in
making macrology easy. Once one has mastered the syntax (` , @), writing
a macro gets as natural as writing out the code. (In case you are
wondering, in my little example I did not need any other customizations
on the button, so it is hard to make out what the initargs are doign up
there. here is how they would work (also getting a little fancier by
actually disabling the "Remove" button, not just making it do nothing
when pressed, if the gear count is zero):
(mk-button-ex ("Remove" (decf (gear-ct .tkw)))
:fore-color 'red ;; Tcl/Tk will understand
:enabled (c? (plusp (gear-ct .tkw))))
becomes:
(make-instance 'button
:fm-parent *parent*
:text "remove"
:on-command (c? (lambda (self)
(decf (gear-ct .tkw))))
:fore-color 'red
:enabled (c? (plusp (gear-ct .tkw))))
[ps. Do not try that at home, i invented the enabled thing. It really
should be the Tk syntax, which I forget.]
ie, I created mk-button-ex because, jeez, every button I put in a GUI I
/know/ needs its own label and its own command (and the parent thing),
but there are other options, too. They have to be supported if the macro
is to get used all the time (we want that), but I do not want to make
them positional arguments without identifying keywords, because then the
code would be unreadable as well as unwritable (from memory).
Needless to say, there is more macrology in the expansion. One bit of
fun is .tkw. Background: in the GUIs I roll, a widget always knows its
parent. I guess you noticed. Anyway, because of that, a rule can kick
off code to navigate to any other widget and get information, ie, it
pretty much has global scope and that means power. Now one place a rule
will often look is up the hierarchy, say to a containing radio group for
a radio button. So the first thing I wrote was:
(upper self radio-group) -> (container-typed self 'radio-group)
Hmmph. I am /always/ upper-ing off self, I am surprised I have not
written a macro so I can just do (^upper radio-group). Soon. But since I
often look to the containing window in rules, I was looking for
something insanely short, shorter than even parentheses would allow,
like ".tkw":
(define-symbol-macro .tkw (nearest self window))
Nearest is like "upper" except that it is inclusive of the starting
point of the search (and, no, I am not happy with the name <g>).
And so it goes with Lisp macros. All the tedious boilerplate is hiiden,
in ways that cannot be done with functions. Oh, I skipped that point.
Look again at how the command (decf (gear-ct .tkw)) gets spliced into
the lambda form as so much source code:
From:
(mk-button-ex ("Remove" (decf (gear-ct .tkw))))
To:
(make-instance 'button
:fm-parent *parent*
:text "remove"
:on-command (c? (lambda (self)
(decf (gear-ct .tkw))))))
If mk-button-ex were a function, Lisp would try to evaluate
(decf (gear-ct .tkw))
which would be pretty sad because the "self" in there would not even
exist yet (the form is an /input/ to make-instance of what will become
"self" by the time the macro expansion code runs.
Which brings us to the idea of every multi-line lambda needing a name.
Does this:
(lambda (self)
(when (plusp (gear-ct .tkw))
(decf (gear-ct .tkw)))
Not sure what the Python would be, but maybe:
lambda (self):
if nearest(self,'window').gear_ct > 0:
nearest(self,'window').gear_ct = \
nearest(self,'window').gear_ct - 1
Does that need a name?
kenny
--
Cells: http://common-lisp.net/project/cells/
"Have you ever been in a relationship?"
Attorney for Mary Winkler, confessed killer of her
minister husband, when asked if the couple had
marital problems.
More information about the Python-list
mailing list