""" Python module for doing dataflow-oriented programming. Written by Kimberley Burchett in August 2004. http://www.kimbly.com/ """ class ripple_or_rock: def __init__(self): self.observers = [] def add_observer(self, observer): self.observers.append(observer) def notify_observers(self): for observer in self.observers: observer.update() def __eq__(self, other): result = ripple(lambda: self.get() == other.get(), "(%s == %s)" % (`self`, `other`)) self.add_observer(result) other.add_observer(result) return result def __lt__(self, other): result = ripple(lambda: self.get() < other.get(), "(%s < %s)" % (`self`, `other`)) self.add_observer(result) other.add_observer(result) return result def __gt__(self, other): result = ripple(lambda: self.get() > other.get(), "(%s > %s)" % (`self`, `other`)) self.add_observer(result) other.add_observer(result) return result def __add__(self, other): result = ripple(lambda: self.get() + other.get(), "(%s + %s)" % (`self`, `other`)) self.add_observer(result) other.add_observer(result) return result def __sub__(self, other): result = ripple(lambda: self.get() - other.get(), "(%s - %s)" % (`self`, `other`)) self.add_observer(result) other.add_observer(result) return result def __mul__(self, other): result = ripple(lambda: self.get() * other.get(), "(%s * %s)" % (`self`, `other`)) self.add_observer(result) other.add_observer(result) return result def __div__(self, other): result = ripple(lambda: self.get() / other.get(), "(%s / %s)" % (`self`, `other`)) self.add_observer(result) other.add_observer(result) return result def __call__(self, *args, **kwargs): result = ripple(lambda: self.get()(*args, **kwargs).get(), # TODO: we're assuming here that the underlying value is actually # a function, and so we can get it's func_name. But that's not # necessarily true... "%s(%s)" % (self.get().func_name, ",".join(map(repr, args)))) self.add_observer(result) for arg in args: arg.add_observer(result) return result class ripple(ripple_or_rock): def __init__(self, expression, repr): ripple_or_rock.__init__(self) self.expression = expression self.repr = repr # Note: do NOT evaluate the expression at this point. If the # ripple is the unexecuted branch of an if-statement, it may cause # errors if the ripple is evaluated. def get(self): try: return self.cached_value except AttributeError: self.cached_value = self.expression() return self.cached_value def update(self): # Note: do NOT evaluate the expression at this point, for the same # reason why we didn't evaluate it in __init__. Instead, we just # clear out any value left over from a previous evaluation. if self.__dict__.has_key("cached_value"): del self.__dict__["cached_value"] self.notify_observers() def __repr__(self): return self.repr class rock(ripple_or_rock): def __init__(self, value=None, name=None): ripple_or_rock.__init__(self) self.value = value self.name = name def get(self): return self.value def set(self, new_value): self.value = new_value self.notify_observers() def __repr__(self): if self.name: return self.name return "rock("+`self.value`+")" def _if(predicate, iftrue, iffalse): def do_if(): if predicate.get(): return iftrue.get() else: return iffalse.get() result = ripple(do_if, "(if %s then %s else %s)" % (predicate, iftrue, iffalse)) predicate.add_observer(result) iftrue.add_observer(result) iffalse.add_observer(result) return result x = rock(1, "x") y = rock(2, "y") one = rock(1) two = rock(2) def recursive_factorial(n): return _if(n < two, one, n*rock(recursive_factorial)(n-one)) fact1 = recursive_factorial(x + y) print 'recursive factorial(%d + %d) == %s' % (x.get(), y.get(), fact1.get()) x.set(3) print 'recursive factorial(%d + %d) == %s' % (x.get(), y.get(), fact1.get()) # TODO: can't figure out how to make a dataflow version of this function... def iterative_factorial(n): result = 1 while (n > 1): result *= n n -= 1 return result