Package: curry
OverviewThis library provides a mechanism to use partial application on bound
methods using AST (abstract syntax tree) transformation. You can use
partial application as an optimization method, an alternative to code
generation (either indirectly or through the AST abstraction).The concrete approach lies somewhere between “currying” and
cooking. We interpret the Python-code manually by walking the AST,
following standard Python logic (literally interpreting every
node). Computations that can be carried out while walking are saved
for later reference (and discarded when possible). When possible,
computations are carried out using the running interpreter.UsageYou use thecookfunction on any bound method.>>> from curry import cook
>>> cooked = cook(some_bound_method)ExampleThe starting point is an instance of some class:>>> class Example(object):
...     def __init__(self, value):
...         self.value = value
...
...     def __call__(self, other):
...         return self.value + otherWe can then use partial application on the bound call-method of some
instance:>>> from curry import cook
>>> example = Example(1)
>>> cooked = cook(example.__call__)
>>> cooked(1)
2Additional examples are included as part of a functional test suite;
they also serve as documentation and are a good starting point to
learn when and how to use partial application.PicklingThe library includes an alternative to the built-inpicklemodule
(and its C-extension counterpart):>>> from curry import dumps, loads
>>> p = dumps(some_object)
>>> some_object = loads(p)What makes these functions interesting is their performance and space
properties. The “pickles” generated are typically 50% smaller and may
be up to 50% faster to unpickle (or up to 200% slower). There’s a
catch: the pickling operation itself is some twenty times slower.This pickle implementation is experimental at best, but possibly
suitable in situations where you unpickle much more frequently than
you pickle. For instance, it could be used for an object data store
with frequent reads.Be aware that the pickles generated are vulnerable to
incompatibilities between interpreter versions (it uses themarshalmodule to serialize code).AuthorThis library is developed and maintained by Malthe Borch
<mborch@gmail.com>. The AST utility library was adapted from the
Genshi codebase and is copyright (c) by 2008 Edgewall Software.This software is released and maintained under the BSD license.Liner notesPartial application is an entirely logical proposition; in theory, it
should work on any method. To actually benefit from it, classes must
be written in a suitable way. This section details the technical
considerations that will guide the developer to a correct
implementation.Object stateAn abstract syntax tree consists of nodes corresponding to the Python
grammar. The expression nodes represent a computation which will
eventually be carried out. Expression nodes that may be evaluated
during partial application are termedresolved. In terms of
the tree structure, it’s only possible to resolve nodes that contain
subnodes that are resolved.When an expression node is resolved, it’s replaced by a custom node
classResolved, which holds the computed value (may be any Python
object). As a technical detail, resolved nodes are not assigned a
symbol name until they are visited by the code generator transform;
internally, they are referenced using their object identity.As a result of evaluation during partial application, we will often
have to instantiate new (stateful) objects. Initially, the state of
these objects is known. The AST may also contain names that correspond
to variable input. Such varibles are undefined at the time of partial
application. When an undefined variable touches a known object, the
state of that object becomes undefined, too:>>> out = []
>>> out.append("Hello")
>>> out.append(string)
>>> "".join(out)Ifstringis undefined, this code reduces logically to the
following, after partial application:>>> out = ["Hello"]
>>> out.append(string)
>>> "".join(out)The state of the list is recorded at the time where it’s state is
first undefined.  This will be described in more detail later.State invalidationThe known state of an object is invalidated if it touches an undefined
object. It can only happen on the invokation of a method, but to this
extent we must be careful to note the way Python implements its
grammar. All operations are passed on to special methods and this
obviously qualifies as method invokation:>>> out = ["Hello"]
>>> out += stringThis invokes the__radd__method on the list object. Sincestringis undefined, it invalidates the known state of the list.As an optimization, the transformer knows about a number of built-in
methods that are static (the state of the object never changes on
invokation):>>> out = ["Hello"]
>>> out.count(string)The staticcountmethod merely counts the occurrances of a given
string. We do not have to invalidate the list.It’s important to note that any object that (transitively) references
an undefined object must itself by undefined.Restoring object stateIn the examples above, the mutable list object is seen as to be
restored by using the list constructor, providing the elements that
are known. This is essentially an optimization. In the general case,
we could use pickles the save and restore state, but this turns out to
be very inefficient.Rather, since the complete state of a resolved object is known, we can
restore the state using the__new__constructor and by setting the
instance dictionary.Instances of builtin primitives likelistandsetdo not have
an instance dictionary. Their state is their value. We handle these as
special cases for optimal performance. The other cases are as
follows:Classes derived from mutable builtins. We instantiate the instance
by calling the__new__constructor of the builtin (passing the
class), then set the state using the builtin__init__method and
finally restore the instance dictionary.Classes derived fromobject. We may instantiate the instance by
calling the__new__constructor ofobject(passing the
class), then restore the instance dictionary.Either of the above that define the__slots__property. Instead of
restoring the instance dictionary, we set each attribute.To save an object’s state in general we transitively generate logic to
instantiate and update the state of its subobjects (contained in the
object dictionary). Note that this logic is then itself equivalent to
a Python pickle, although spelled out as executable code.Dynamic evaluationTheexecstatement and theevalfunction behave
differently. The string argument are parsed as respectively a
statement and an expression and inserted as-is into the AST.Applications include dynamic (local) variable assignment and
expression evaluation, both integral to template processing.Function callsThere are four different scenarios. Either the function is known, or
the arguments are known, or both, or neither.If both the function and its arguments are known, we compute the
result immediately.If the function is known, but not the arguments, we include the
function as a closure and process partial application on it.To-DoStar arguments and double-star keyword arguments.Ability to mark a method as “static”, e.g. using a decorator.Changelog0.2 (released 2009/07/29)Features:Added pickling capability (experimental), both as standalone
functions and integrated with cooking machinery to preserve and
restore object state.Bugfixes:Make sure assignments are made to names only.0.1.1 (released 2009/06/02)Fixed documentation formatting.Removed debugging statements.0.1 (released 2009/06/02)Initial public release.