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

Ken Tilton kentilton at gmail.com
Mon May 8 00:51:57 EDT 2006



Alexander Schmolck wrote:
> [trimmed groups]
> 
> Ken Tilton <kentilton at gmail.com> writes:
> 
> 
>>yes, but do not feel bad, everyone gets confused by the /analogy/ to
>>spreadsheets into thinking Cells /is/ a spreadsheet. In fact, for a brief
>>period I swore off the analogy because it was so invariably misunderstood.
>>Even Graham misunderstood it.
> 
> 
> Count me in.

<g> But looking at what it says: "Think of the slots as cells in a 
spreadsheet (get it?), and you've got the right idea. ", if you follow 
the analogy (and know that slot means "data member" in other OO models) 
you also know that Serge's Spreadsheet example would have scored a big 
fat zero on the Miller Analogy Test. Serge in no way made slots in 
Python classes behave like cells in a spreadsheet. He simply started 
work on a Spreadsheet application, using Python classes along the way. Bzzt.

While everyone makes the mistake, it is only because few of us (me 
included) read very carefully. Especially if they are more interested in 
flaming than learning what someone is saying.

C'mon, people. I linked to Adobe's betting the ranch on such an idea. I 
linked to Guy Steele's paper on the same idea. In which he marvelled 
that it had not caught on. I could also link you to COSI over at STSCI, 
presented at a Lisp Users Group Meeting in 1999 where they were jumping 
up and down about the same thing. One of my users gets Cells because he 
loved the KR system in Garnet. Look it up. I have more citations of 
prior art. And, again, it has gone mainstream: Adobe has adopted the 
paradigm.

Y'all might want to ease up on the pissing contest and learn something. 
or not, I have been on Usenet before. :)


> 
> 
>>But it is such a great analogy! <sigh>
>>
>>>but what's the big deal about PyCells?
>>>Here is 22-lines barebones implementation of spreadsheet in Python,
>>>later I create 2 cells "a" and "b", "b" depends on a and evaluate all
>>>the cells. The output is
>>>a = negate(sin(pi/2)+one) = -2.0
>>
>>>b = negate(a)*10 = 20.0
>>
>>Very roughly speaking, that is supposed to be the code, not the output. So you
>>would start with (just guessing at the Python, it has been years since I did
>>half a port to Python):
>>
>>
>>   v1 = one
>>   a = determined_by(negate(sin(pi/2)+v1)
>>   b = determined_by(negate(a)*10)
>>   print(a) -> -2.0 ;; this and the next are easy
>>   print(b) -> 20
>>   v1 = two ;; fun part starts here
>>   print(b) -> 40 ;; of course a got updated, too
>>
> 
> 
> do you mean 30?
> 
> I've translated my interpretation of the above to this actual python code:
> 
> from math import sin, pi
> v1 = cell(lambda: 1)
> a = cell(lambda:-(sin(pi/2)+v1.val), dependsOn=[v1])
> b = cell(lambda: -a.val*10, dependsOn=[a], 
>          onChange=lambda *args: printChangeBlurp(name='b',*args))
> print 'v1 is', v1
> print 'a is', a # -2.0 ;; this and the next are easy
> print 'b is', b # 20
> v1.val = 2 # ;; fun part starts here
> print 'v1 now is', v1
> print 'b now is', b # 30 ;; of course a got updated, too
> 
> 
> I get the following printout:
> 
> v1 is 1
> a is -2.0
> b is [cell 'b' changed from <__main__.unbound object at 0xb4e2472c> to 20.0,
> it was not bound]20.0
> [cell 'b' changed from 20.0 to 30.0, it was bound ] v1 now is 2
> b now is 30.0
> 
> Does that seem vaguely right?

<g> You have a good start. But you really have to lose the manual wiring 
of dependencies, for several reasons;

  -- it is a nuisance to do
  -- it will be a source of bugs
  -- it will be kind of impossible to do, because (in case you missed 
it), the rule should be able to call any function and establish a 
dependency on any other cell accessed. So when coding a change to a 
function, one would have to go track down any existing rule to change 
its dependsOn declaration. never mind the pain in th first place of 
examining the entire call tree to see what else gets acessed.
-- it gets worse. I want you to further improve your solution by 
handling rules such as this (I will just write Lisp):

      (if (> a b)
         c ;; this would be the "then" form
         d)) ;; this is the 'else'

The problem here is that the rule always creates dependencies on a and 
b, but only one of c and d. So you cannot write a dependsOn anyway 
(never mind all the other reasons for that being unacceptable).

> 
> 
>>The other thing we want is (really inventing syntax here):
>>
>>   on_change(a,new,old,old-bound?) print(list(new, old, old-bound?)
> 
> 
> Is the above what you want (you can also dynamically assign onChange later
> on, as required or have a list of procedures instead)?

Your onChange seems to be working fine. One thing we are glossing over 
here is that we want to use this to extend the object system. In that 
case, as I said and as no one bothered to comprehend, we want /slots/ to 
behave like spreadsheet cells. Not globals. And I have found that these 
onChane deals are most sensibly defined on slots, not cell by cell.

That said, if you did work something up similar for Python classes, i 
have no doubt you could do that.

Again, if anyone is reading and not looking to just have a flamewar, 
they will recall i have already done once a partial port of Cells to 
python. (I should go look for that, eh? It might be two computer systems 
back in the closet though. <g>)

> 
> 
>>Then the print statements Just Happen. ie, It is not as if we are just hiding
>>computed variables behind syntax and computations get kicked off when a value
>>is read. Instead, an underlying engine propagates any assignment throughout
>>the dependency graph before the assignment returns.
> 
> 
> Updating on write rather than recalculating on read does in itself not seem
> particularly complicated.

<heh-heh> Well, there are some issues. B and C depend on A. B also 
depends on C. When A changes, you have to compute C before you compute 
B, or B will get computed with an obsolete value of C and be garbage. 
And you may not know when A changes that there is a problem, because as 
you can see from my example (let me change it to be relevant):

     (if (> a d) c e)

It may be that during the prior computation a was less= d and did /not/ 
depend on c, but with the new value of a is > d and a new code branch 
will be taken leading to c.

It was not that hard to figure all that out (and it will be easier for 
you given the test case <g>) but I would not say propagation is 
straightforward. There are other issues as well, including handling 
assignments to cells within observers. This is actually useful 
sometimes, so the problem needs solving.

> 
> 
>>My Cells hack does the above, not with global variables, but with slots (data
>>members?) of instances in the CL object system. I have thought about doing it
>>with global variables such as a and b above, but never really seen much of
>>need, maybe because I like OO and can always think of a class to create of
>>which the value should be just one attribute.
> 
> 
> OK, so in what way does the quick 35 line hack below also completely miss your
> point?

What is that trash talking? I have not seen your code before, so of 
course I have never characterized it as completely missing the point. 
Spare me the bullshit, OK?

Alexander, you are off to a, well, OK start on your own PyCells. You 
have not made a complete mess of the low-hanging fruit, but neither have 
you done a very good job. Requiring the user to declare dependencies was 
weak -- I never considered anything that (dare I say it?) unscaleable. 
Like GvR with Python, I knew from day one that Cells had to very simple 
on the user. Even me, their developer. But do not feel too bad, the GoF 
Patterns book described more prior art (I might have mentioned) and they 
had explicit (and vague) subscribe/unsubscribe requirements.

As for the rest of your code, well, propagation should stop if a cell 
recomputes the same value (it happens). And once you have automatic 
dependency detection, well, if the rule is (max a b) and it turns out 
that b is just 42 (you had cell(lambda 1)... why not just cell(1) or 
just 1), then do not record a dependency on b. (Another reason why the 
user cannot code dependsOn -- it is determined at run time, not by 
examination of the code.

Now we need to talk about filters on a dependency....

kenny (expecting more pissing and less reading of the extensive on-line 
literature on constraints)


-- 
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