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

Alexander Schmolck a.schmolck at gmail.com
Sun May 7 12:45:24 EDT 2006

```[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.

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

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

>
> 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
> the dependency graph before the assignment returns.

Updating on write rather than recalculating on read does in itself not seem
particularly complicated.

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

# (NB. for lispers: 'is' == EQ; '==' is sort of like EQUAL)

def printChangeBlurp(someCell, oldVal, newVal, bound, name=''):
print '[cell %r changed from %r to %r, it was %s]' % (
name, oldVal, newVal, ['not bound', 'bound '][bound]),

_unbound = type('unbound', (), {})() # just an unique dummy value
def updateDependents(dependents):
seen = {}
for dependent in dependents:
if dependent not in seen:
seen[dependent] = True
dependent.recalculate()
updateDependents(dependent._dependents)
class cell(object):
def __init__(self, formula, dependsOn=(), onChange=None):
self.formula = formula
self.dependencies = dependsOn
self.onChange = onChange
self._val = _unbound
for dependency in self.dependencies:
if self not in dependency._dependents:
dependency._dependents.append(self)
self._dependents = []
def __str__(self):
return str(self.val)
def recalculate(self):
newVal = self.formula()
if self.onChange is not None:
oldVal = self._val
self.onChange(self, oldVal, newVal, oldVal is not _unbound)
self._val = newVal
def getVal(self):
if self._val is _unbound:
self.recalculate()
return self._val
def setVal(self, value):
self._val = value
updateDependents(self._dependents)
val = property(getVal, setVal)

'as

```