Weaver/Yarn Pattern in Python

Paul Prescod paul at prescod.net
Wed Jan 21 10:24:41 CET 2004


I've tried to understand your problem statement in detail but it is alot 
to keep in the head in pure abstraction (i.e. I don't have your specific 
problem and haven't worked on this complicated a tree walker in a while 
if ever). Also, I think there may have been some typos that are making 
it harder to understand. For instance, is visitB supposed to be 
syntactically identical to visitA except it calls weaveB's instead of 
weaveA's? Is this the boilerplate that offends you?

But maybe I can help anyhow. Here's some code from your post:

 > class Weaver:
 >     "A special visitor which weaves yarns"
 >     def __init__(self, yarns):
 >         self.yarns = yarns
 >     def visitA(self, a):
 >         for y in self.yarns:
 >             y.weaveA_First(a)
 >         for i, k in enumerate(a.kids):
 >             for y in self.yarns:
 >                 y.weaveA_Before(a, i)
 >             k.accept(self)
 >             for y in self.yarns:
 >                 y.weaveA_After(a, i)
 >         for y in self.yarns:
 >             y.weaveA_First(a)
 >     def visitB(self, b):
 >         for y in self.yarns:
 >             y.weaveA_First(b)
 >         for y in self.yarns:
 >             y.weaveA_First(b)

I'm going to presume that visitB was supposed to be more like:

 >     def visitB(self, b):
 >         for y in self.yarns:
 >             y.weaveB_First(b)
 >            ... identical to visitA except A's swapped for B's ...

If it isn't identical to visitA (except for the B) then I don't know 
what the boilerplate is. Also, you end visitA with a weaveA_First but 
you probably mean weaveA_Last.

If I'm understanding right, I think each yarn has a two-D matrix of methods:

           A        B          C        D  ....
First
Before
After
Last

And then you've got another dimension for the yarns (that varies at runtime)

There are many ways to represent these axes in Python:

self.yarns[0]["A"]["First"]
self.yarns[0]["A", "First"]
self.yarns[0]["A_First"]
self.yarns[0]["A"].First()


class Weaver:
     "A special visitor which weaves yarns"
     def __init__(self, yarns):
          self.yarns = yarns
     def visit(self, a):
          for y in self.yarns:
              y.threads[a.type].First(a)
          for i, k in enumerate(a.kids):
              for y in self.yarns:
                  y.threads[a.type].Before(a, i)
              k.accept(self)
              for y in self.yarns:
                  y.threads[a.type].After(a, i)
          for y in self.yarns:
              y.threads[a.type].Last(a)

Now a yarn becomes essentially a holder for a dictionary of what I've 
called threads (that'll confuse your co-workers!).

And a thread works with a particular node type, doing the First, Before, 
After and Last for it.

The "thread" looks something like:



class X_Yarn_A_thread:
	self.type == "A"
	def First(self, arg):
		assert arg.type == self.type
		return self.first(arg)

	def Before(self, arg):
		assert arg.type == self.type
		return self.before(arg)

The asserts may be useless considering how bullet-proof the Weaver code 
should be...you dispatch on a.type so how could a thread end up with the 
wrong type?

Hacking together method names with strings is only ever a sort of 
syntactic sugar for some other design that uses dictionaries "properly". 
Remember that Python has at its core a similar feature set to Scheme and 
Lisp which are famously "flexible" and "powerful" and yet do not do any 
name manging weirdness. It's great that Python allows that name manging 
stuff but if you feel you HAVE to use it then you probably aren't 
thinking in terms of higher order functions or objects in contrainers as 
you could/should. You should think about hwo to do things without 
method-name hacking before deciding that that's a better solution for 
some reason of expedience or usability.

  Paul Prescod





More information about the Python-list mailing list