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.