Sunday, January 22, 2006

A Different Implementation of Relation-Chaining

OK, I wasn't really happy with the implemenation the relation-chaining thing I did last week, so I rewrote it. This won't make a lot of sense if you haven't read last week's installment, so go read it if you haven't yet...

This time I have the forward-linking operator sort of equivalent to the python "." operator. If romulus.father == mars, then romulus >> father == rset([mars]); in other words, the set containing mars. rset is a class derived from set. It's convenient to work with sets, because in the case of the reverse-linking operator, there could be more than one result, and I wanted some symmetry between the two cases.

To do the reverse linking, I use gc.get_objects(), which is python's garbage collector's master list of all the objects in the system. I guess that's not very efficient for a large program. In a large program I'd recommend creating a "universe" dictionary, put the objects you really care about it in it, then search that instead of get_objects().

A nice result of using sets is shown in the brother() function; the set operator "-" means to remove items from a set, so (x >> "father" << "father") - x means: find the father of x, then find the people x is a father to, then subtract x from that set, leaving only the siblings.

The only reason I create my own set class, or my own relational_object class, is to get the syntax of << and >> to work. Really, this is a demonstration of a feature I think could be central to a language, and in that hypothetical language you'd build this feature in so it could be used on any objects.

Well, here's the code. I promise I'll move on to something different next week! I just had to get this out of my system.

#!/usr/local/bin/python2.4
from gc import get_objects

class relational_object(object):
def __repr__(self):
return ""

def get_conv_gen(self, att):
for other in get_objects():
try:
if other.__dict__[att] == self:
yield other
except Exception, e: pass

def get_conv_any(self, att): return self.get_conv_gen(att).next()
def get_conv_all(self, att): return rset([ x for x in self.get_conv_gen(att)])

def __rshift__(self, att): return rset([self.__dict__[att]])
def __lshift__(self, att): return rset(self.get_conv_all(att))

class rset(set):
def __rshift__(self, att):
result = rset()
for x in self:
result = result | rset([x.__dict__[att]])
return result
def __lshift__(self, att):
result = rset()
for x in self:
result = result | x.get_conv_all(att)
return result

class person(relational_object):
def __init__(self, theName):
self.name = theName
def __repr__(self): return "<>"

mars = person("mars")
romulus = person("romulus")
remus = person("remus")
assert mars == mars
assert mars != remus

romulus.father = mars
remus.father = mars

assert mars << "father" == rset([romulus, remus])

No comments: