A critic of Guido's blog on Python's lambda

Ken Tilton kentilton at gmail.com
Tue May 16 18:30:02 CEST 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