Sundar Narasimhan posted to ll1-discuss, under the subject line "Re: how expressive are they?", about a really compelling application of macros in software he worked on. He said: > The big [macro that doesn't easily become a higher-order function] in > our system is really similar to the define-class that I think Dan > Weinreb pointed out. We have a similar facility that defines classes > based on database tables.. that expands into classes/methods that > define object managers (consider that you may want to maintain a > hash-table of objects in your database that is indexed by it's primary > keys -- which are often n columns where n > 1 :), select/update/delete > transactions, even a matcher function so you can say things like > (lookup-objects object-manager :some-column value1 :some-other-column > value2). That alone saves us oodles of code, but the system also > modifies setf and uses clos/flavors facilities to introduce > "write-restriction" clauses.. so that when a programmer creates an > object of this flavor and does (setf (some-field o) value) the db code > actually kicks in to enqueue transactions to the server etc. And it > does things like associations etc. (should a parent be deleted when > all the children of an n-ary relationship get deleted?) Pretty cool > stuff. (It's like having a Perl facility to modify the semantics of > what Perl does when you do "s = foo;" or "s =~ bar;" :)
So I did most of what he described in Python, without macros. #!/usr/bin/python import weakref def test(): person = define_class('firstname', 'lastname', 'shoesize', 'engine_displacement_cc', primary_key=('firstname', 'lastname')) bob = person('Robert', 'Johnson', 13, 50) angela = person('Angela', 'Freggen', 8, 1700) assert bob.firstname == 'Robert' assert bob == bob assert bob != angela person('Robbie', 'Johnson', 13, 50) assert bob.primary_key() == ('Robert', 'Johnson') assert bob == person.lookup('Robert', 'Johnson') assert bob != person.lookup('Angela', 'Freggen') assert [angela] == person.lookup(shoesize=8) assert [bob, angela] == person.lookup() angela.lastname = 'Hogg' # she got married assert person.transaction_log() == [ ('insert', ('Robert', 'Johnson', 13, 50)), ('insert', ('Angela', 'Freggen', 8, 1700)), ('insert', ('Robbie', 'Johnson', 13, 50)), ('delete', ('Robbie', 'Johnson', 13, 50)), ('update', ('Angela', 'Freggen', 8, 1700), ('Angela', 'Hogg', 8, 1700)), ], person.transaction_log() class instance: def __cmp__(self, other): dircmp = cmp(dir(self), dir(other)) if dircmp != 0: return dircmp return cmp(self.value_tuple(), other.value_tuple()) def primary_key(self): return tuple([getattr(self, fieldname) for fieldname in self.primary_key_fields]) def value_tuple(self): return tuple([getattr(self, fieldname) for fieldname in self.fields]) def __setattr__(self, field, value): before = self.value_tuple() self.__dict__[field] = value after = self.value_tuple() self.klass.log(('update', before, after)) def __del__(self): self.klass.log(('delete', self.value_tuple())) class define_class: def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs self.by_primary_key = weakref.WeakValueDictionary() self._transaction_log = [] def log(self, item): self._transaction_log.append(item) def lookup(self, *args, **kwargs): if args: return self.by_primary_key[tuple(args)] else: rv = [] for item in self.by_primary_key.values(): for (field, value) in kwargs.items(): if getattr(item, field) != value: break else: rv.append(item) return rv def __call__(self, *args): rv = instance() for (field, value) in zip(self.args, args): rv.__dict__[field] = value rv.__dict__['fields'] = self.args rv.__dict__['primary_key_fields'] = self.kwargs['primary_key'] rv.__dict__['klass'] = self assert not self.by_primary_key.has_key(rv.primary_key()) self.by_primary_key[rv.primary_key()] = rv self.log(('insert', tuple(args))) return rv def transaction_log(self): return self._transaction_log if __name__ == "__main__": test() -- <[EMAIL PROTECTED]> Kragen Sitaker <http://www.pobox.com/~kragen/> Edsger Wybe Dijkstra died in August of 2002. The world has lost a great man. See http://advogato.org/person/raph/diary.html?start=252 and http://www.kode-fu.com/geek/2002_08_04_archive.shtml for details.