Saturday, August 19, 2006

I'm writing my new Python program in a much more "functional" style than I used to.

I'm using a lot of closures and whatever it is you'd call it that's like a closure but you return an "inner class" from a function, holding on to some of the context. As the contextual things I'm passing in tend to be constructors and basic toString routines, I'm not really sure if this is winning me anything over using inheritance. But it's an interesting exercise. It feels a little bit "wholesome" or cool. Although it's probably deeply unpythonic.

What I am starting to discover is some of the downsides.

Here's the basic situation. I define a TreeMaker function which accepts two arguments. A constructor function and a str.

Inside it, I define a Tree class, which calls the constructor and initializes some infrastructure management stuff (like a list of children, the parent etc). Then there's a bunch of other methods.

Finally I return the Tree class from the TreeMaker function.

def TreeMaker(construct, str) :

class Tree :

def __init__(self,*args) :
self.children = []
self.parent = None

... more functions

return Tree

I can now create new Tree classes by defining their constructors and toString routines and calling TreeMaker :

MyTree = TreeMaker(myConstructor, myStr)

mt = MyTree(blah, blah2, blah3)

MyTree is a new class. mt is a new instance of the class.

It's not clear yet if this buys me much over simply defining Tree as a base-class (or mixin) which would call a "construct" defined in the subclass. Or even using a traditional sub-class that would just call the super-class's constructor explicitly. But I'm going to persevere for a while to see.

There are some issues. Most obviously, the name "MyTree" is just a variable name. If you do mt.__class__ it returns "Tree", knowing nothing about MyTrees as types within the system. All these parameterized versions of Tree really *are* nothing but Trees so you won't be able to tell them apart.

Another issue is how the code is more distributed and diffuse than using classes. If I'm debugging the behaviour of a traditional MyTree subclass of Tree, then it's pretty clear where to look for that behaviour being defined : a) in the MyTree class, and b) in the Tree class. In my system that behaviour could be in the Tree or the constructor or the toString function. Or one of several other free-floating function-factories I'm using to run around the trees.

Here I've seen some more interesting wins. This function takes a copy-function as an argument, and runs around a tree, building a second, isomorphic tree, who's every node is some transformation of the equivalent node in the first.

def TreeProcessor(copyFunc) :

def copier(otherTree, parent=None, filter=lambda x : True) :

if filter(otherTree) :
this = copyFunc(otherTree)
if parent != None :
parent.addChild( this )
for x in otherTree.children :
copier(x, this, filter)
return this
return None

return copier

But is this an improvement on having TreeProcessor be a class, and copier be a method which takes copyFunc as argument?

No comments: