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

Ken Tilton kentilton at gmail.com
Tue May 16 16:08:40 EDT 2006

Lasse Rasinen wrote:
> Ken Tilton <kentilton at gmail.com> writes:
>>If you want to insist on how perfect your code is, please go find
>>ltktest-cells-inside.lisp in the source you downloaded and read the long
>>comment detailing the requirements I have identified for "data integrity".
>>Then (a) tell me how your code fails at integrity, (b) fix it, and (c)
>>tell me again how easy Cells is. :)
> Found it and read it; it was most enlightening. I claim my system fulfills
> the first three requirements(*), while most likely failing gloriously on
> the two last ones.


 From #1: "recompute all and (for efficiency) only state computed off X 
(directly or indirectly through some intermediate datapoint)"

Bzzzt! Another 47hrs, directions from a mentor, and a reference 
implementation and you /still/ do not even understand the requirements, 
let alone have a working port. The good news is that it is not an 
integrity requirement that is being missed, it is the efficiency 
requirement I snuck in there. The bad news is, see below.

Want to find the efficiency shortcoming yourself, or should I tell you? 
You are entitled to the latter given the rules of the game (simulating a 
pythonista student making off with five thousand undeserved dollars). :)

> I'll postpone (b) while I've had a chance to think it over(**), but in the
> face of the evidence I'm willing to admit my earlier work estimates (which
> should have had a smiley next to them anyway ;-) were in error.

Aw, shucks, then I will admit that, before today, I never actually 
looked at your code. :)

Well, I followed the URL and glanced at it, but I missed the use of the 
timestamp. Speaking of which, Holy Granularity, Batman! You use 
Time.time() to determine currency of a computation?!:

     Return the time as a floating point number expressed in seconds 
since the epoch, in UTC. Note that even though the time is always 
returned as a floating point number, not all systems provide time with a 
better precision than 1 second."

One /second/?!!!!! Exactly how slow is Python? I know you guys love that 
issue as much as Lispniks. In un-compiled Lisp:

  CTK(4): (loop repeat 2 do (print (get-internal-real-time)))

And you have no idea how slow PRINT is. btw, I thought Python was 
portable. What is with the Time class and "not all systems..."? Check 
out the Lisp:

(defun zoom ()
   (loop with start = (get-internal-real-time)
         while (= start (get-internal-real-time))
         count 1 into cities-destroyed
         finally (format t "~a cities destroyed in 1/~a of a second"
                   cities-destroyed internal-time-units-per-second)))

internal-time-units-per-second is (from the standard):

"Constant Value:
    A positive integer, the magnitude of which is 
implementation-dependent. "

So we vary, too, but my Lisp has to tell me so I can normalize. Anyway, 
running that repeatedly I get pretty wild variation. My high score is:

    CTK(18): 11637 cities destroyed in 1/1000 of a second

My low was under a thousand! I guess I have to wait until we cross a 
millisecond boundary:

(defun zoom ()
   (symbol-macrolet ((now (get-internal-real-time)))
     (loop with start = (loop for mid = now
                            while (= mid now)
                            finally (return now))
         while (= start now)
         count 1 into cities-destroyed
         finally (format t "~a cities destroyed in 1/~a of a second"

Ok, now I am consistently taking out about 11.5k Russian cities. And you 
need to fix your system.

Just use a counter -- does Python have bignums? if not, you'll have to 
worry about wrapping. (the sound you hear is a project schedule 
slipping. <g>)

> I won't admit to destroying Moscow, though. See (*).

Sorry, you actually /have/ violated the data integrity requirement. You 
confess below to missing this one:

"a corollary: should a client observer SETF a datapoint Y, all the above 
must happen with values current with not just X, but also with the value 
of Y /prior/ to the change to Y."

Well, how can you claim integrity when some values do not get 
recalculated until the world has moved on to state N+2, if you will? 
State N+1 had the information that headed off the launch.

Bye bye, Kremlin.

The easiest way to construct such a scenario would be with an ephemeral 
cell. As you know... oops. Maybe you do not. Well, spreadsheets are 
kinda steady state in orientation. Given a world of other values, this 
is what my value should be. But what about events? You have been using 
your version of PyCells in real-world applications for a while... oops. 
No you have not. Well, when you start trying to handle events from an 
event loop, you will discover a need to model events. (Trust your 
mentor.) You need a slot that can be assigned normally, propagate 
according to the above rules and regulations, and then revert to a null 
state but /not/ as a state change -- neither propagating nor 

They are, like, ephemeral, aka, "fleeting".

Now as I said, I got away with such holes for /years/ in all sorts of 
hairy applications of Cells before finally, well, falling down those 
holes, but it does happen so the holes are really worth filling in.

And wait till you see what this does to your algorithm. :) I was even 
tempted to ban observer writebacks, if you will, but I think someday I 
will write an application driven by such things, periodically calling 
Tcl_DoOneEvent to keep in touch with the outside world.

Which means we cannot propagate on the stack, as your system would. 

> (*) All values depending on the changed cell are marked as invalid before
> anything else is done; trying to access an invalid value forces a
> recalculation which also marks the cell as valid again, and so on
> recursively down to cells that have already converged to a proper value.
> Setting input cells in callbacks will f*** that up, though.
> (**) Which probably doesn't occur until July or something ;(
>>>[Decorator example]
>>Wow. I thought Python did not have macros.
> Calling decorators macros is an insult to CL macros ;-)
> All they do is make it a bit more convinent to apply transformations to
> functions, or as the abstract in the original spec[1] says:
>   The current method for transforming functions and methods (for instance,
>   declaring them as a class or static method) is awkward and can lead to
>   code that is difficult to understand. Ideally, these transformations
>   should be made at the same point in the code where the declaration
>   itself is made.
>>This project is looking better all the time. Thx.
> In that case I have something else you might like: The "with" Statement[2]

I had high hopes for that when I saw it, then was not sure. The good 
news is that Serious Pythonistas will be doing this, not me. I will just 
help them grok the requirements (and hope Ryan is taking notes <g>).

> From the abstract:
>     This PEP adds a new statement "with" to the Python language to make
>     it possible to factor out standard uses of try/finally statements.
> In practice one can use it implement some of the with-macros in a pretty
> straightforward manner, especially those that are expanded into
> (call-with-* (lambda () , at body)). I believe the previously advertised
> with-integrity macro could also be made to work in a satisfying manner.
> To quote from the examples:
>     2. A template for opening a file that ensures the file is closed
>        when the block is left:
>         @contextmanager
>         def opened(filename, mode="r"):
>             f = open(filename, mode)
>             try:
>                 yield f
>             finally:
>                 f.close()
>        Used as follows:
>         with opened("/etc/passwd") as f:
>             for line in f:
>                 print line.rstrip()
> Does that last part look familiar to you?)
> The problem with this is that this is Python 2.5 syntax, which currently
> appears to be planned for release in late August, a bit late for SoC.
> Alpha(*) versions are available, so if you want to take a chance and live
> on the bleeding edge, you can probably gain from it.

That is up to the Pythonistas. As a rule, if there is advantage to be 
had, I go with the bleeding edge until it proves to me it is unusable.

Thx for the heads up.

Nice job on the code, btw. A lot better than my first efforts.


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