[PEAK] Prototype generic functions now available in CVS

Phillip J. Eby pje at telecommunity.com
Tue Jul 13 19:06:06 EDT 2004

The CVS trunk version of PyProtocols and PEAK now supports generic 
functions using 'when()', e.g.:

     from protocols.dispatch import when

     [when("x in int and y in str")]
     def do_something(x,y):
         # body here will be executed when 'x'
         # is an integer and 'y' is a string

If 'do_something' is an existing generic function, the function body is 
added to it, for execution when the condition is true.  If there is 
currently no 'do_something' generic function, it is created.  So, you can 
in one module define a few initial cases for a generic function, and then 
extend it in another, like so:

     from other_module import do_something

     [when("some new condition here")]
     def do_something(x,y):
         # etc.

Note that this *modifies* the imported generic function, so the new case 
will be used whenever the original generic function is called, as long as 
the condition is the most-specific applicable one.

This is an extremely early release, and there's still a long to-do 
list.  For example, you can't refer to varargs or keyword args in your 
when() conditions.  There's no way to make "real" variables (i.e. not have 
names looked up at function definition time), except by having them not be 
defined yet.  Everything is pure Python, and probably rather slow.  There's 
no real method combining yet.  Lamdbas and list comprehensions aren't 
allowed in 'when()' expressions.  And so on.

Based on the total implementation size, (~2700 lines), this actually 
doubles the current size of PyProtocols, making it probably sensible to 
give it its own package.  :)  It's indeed half the size of the pure-Python 
'compiler' package, which I guess makes sense since it is indeed a compiler 
of sorts for about half the Python language.

One caveat: ordering of operations among subexpressions is not currently 
guaranteed.  If you write something like:

     [when("y<>0 and x/y>23")]

it's potentially possible that these conditions will be evaluated in a 
different order, making it possible to get a ZeroDivisionError.  Fixing 
this is one of the next things up on the to-do list, though, and it's 
pretty much just the writing of some rather tedious tests and 
code.  (Specifically, to ensure that Signature operations are always 
order-preserving, and to have GenericFunction objects keep an acyclic 
constraint graph between expressions that share common variables, using it 
to restrict its choice of the "best expression to test next".)

Oh, and don't forget what I said in a recent e-mail: the 'when()' 
expression language doesn't execute exactly the way Python does, even 
though it's Python syntax.  Anything that *can* be computed when the 
expression is parsed, *will* be.  If you say something like "x > 
foo.bar[baz]", and 'foo' and 'baz' are not arguments to the generic 
function, then 'foo.bar[baz]' will be computed at *parse time*.  If 'baz' 
is an argument, then 'foo.bar' will be computed at parse time.  Only 
subexpressions involving function arguments will be computed when the 
generic function is invoked.

In general, that's a really good thing, but it basically means you should 
make sure that anything you reference in the condition already has the 
value you want, at the time of the 'when()' call.  Alternatively, you can 
make sure the variable(s) are *not* defined, in which case it will force 
calculation at runtime.

(Hmmm...  which means that if you typo, you won't find out about it until a 
case that needs to compute that expression happens.  So maybe there should 
be some kind of warning unless you specifically identify variable portions 
of the expression.  It seems worth thinking about, anyway.)

More information about the PEAK mailing list