[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")]
# 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")]
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