# 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:
for i, k in enumerate(a.kids):
for y in self.yarns:
k.accept(self)
for y in self.yarns:
for y in self.yarns:

Now a yarn becomes essentially a holder for a dictionary of what I've

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

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

```