[Python-Dev] continuations for the curious

Sam Rushing rushing@nightmare.com
Tue, 27 Jul 1999 03:33:03 -0700 (PDT)


--IuOx2/xRLE
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

I've been playing for a bit, trying to write my own coroutine class
(obeying the law of "you won't understand it until you write it
yourself") based on one I've worked up for 'lunacy'.

I think I have it, let me know what you think:

>>> from coroutine import *
>>> cc = coroutine (counter, 100, 10)
>>> cc.resume()
100
>>> cc.resume()
110
>>>

Differences:
1) callcc wraps the 'escape frame' with a lambda, so that
   it can be invoked like any other function.  this actually
   simplifies the bootstrapping, because starting the function
   is identical to resuming it.

2) the coroutine object keeps track of who resumed it, so that
   it can resume the caller without having to know who it is.

3) the coroutine class keeps track of which is the currently
   'active' coroutine.  It's currently a class variable, but
   I think this can lead to leaks, so it might have to be made
   a global.

   +-----------------------------------------------------------------
   | For those folks (like me) that were confused about where to get
   | all the necessary files for building the latest Stackless Python,
   | here's the procedure:
   | 
   | 1) unwrap a fresh copy of 1.5.2
   | 2) unzip
   |     http://www.pns.cc/anonftp/pub/stackless_990713.zip
   |    on top of it
   | 3) then, unzip
   |     ftp://ftp.pns.cc/pub/veryfar.zip
   |    on top of that
   | 4) add "continuation continuationmodule.c" to Modules/Setup

-Sam


--IuOx2/xRLE
Content-Type: text/plain
Content-Description: coroutine.py
Content-Disposition: inline;
	filename="coroutine.py"
Content-Transfer-Encoding: 7bit

# -*- Mode: Python; tab-width: 4 -*-

import continuation

def callcc (fun, *args, **kw):
	k = continuation.getpcc(2) # eerie, that numeral two
	kfun = lambda v,k=k: continuation.putcc (k, v)
	return apply (fun, (kfun,)+args, kw)

class coroutine:

	current = None

	def __init__ (self, f, *a, **kw):
		self.state = lambda v,f=f,a=a,kw=kw: apply (f, a, kw)
		self.caller = None

	def resume (self, value=None):
		caller = coroutine.current
		callcc (caller._save)
		self.caller = caller
		coroutine.current = self
		self.state (value)
		
	def _save (self, state):
		self.state = state

def resume_caller (value):
	me = coroutine.current
	me.caller.resume (value)

def resume_main (value):
	main.resume (value)

main = coroutine (None)
coroutine.current = main

# counter/generator

def counter (start=0, step=1):
	n = start
	while 1:
		resume_caller (n)
		n = n + step

# same-fringe

def _tree_walker (t):
	if type(t) is type([]):
		for x in t:
			_tree_walker (x)
	else:
		resume_caller (t)

def tree_walker (t):
	_tree_walker (t)
	resume_caller (None)

def same_fringe (t1, t2):
	co1 = coroutine (tree_walker, t1)
	co2 = coroutine (tree_walker, t2)
	while 1:
		leaf1 = co1.resume()
		leaf2 = co2.resume()
		print 'leaf1: %s leaf2: %s' % (leaf1, leaf2)
		if leaf1 == leaf2:
			if leaf1 is None:
				return 1
		else:
			return 0

--IuOx2/xRLE--