A critic of Guido's blog on Python's lambda
kentilton at gmail.com
Mon May 8 06:51:57 CEST 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
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
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)
"Have you ever been in a relationship?"
Attorney for Mary Winkler, confessed killer of her
minister husband, when asked if the couple had
More information about the Python-list