Welcome, guest | Sign In | My Account | Store | Cart

Notice! PyPM is being replaced with the ActiveState Platform, which enhances PyPM’s build and deploy capabilities. Create your free Platform account to download ActivePython or customize Python with the packages you require and get automatic updates.

Download
ActivePython
INSTALL>
pypm install curry

How to install curry

  1. Download and install ActivePython
  2. Open Command Prompt
  3. Type pypm install curry
 Python 2.7Python 3.2Python 3.3
Windows (32-bit)
0.2 Available View build log
Windows (64-bit)
0.2 Available View build log
Mac OS X (10.5+)
0.2 Available View build log
Linux (32-bit)
0.2 Available View build log
Linux (64-bit)
0.2 Available View build log
 
Author
License
BSD
Imports
Lastest release
version 0.2 on Jan 5th, 2011

Overview

This 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.

Usage

You use the cook function on any bound method.

>>> from curry import cook
>>> cooked = cook(some_bound_method)
Example

The starting point is an instance of some class:

>>> class Example(object):

System Message: ERROR/3 (<string>, line 30)

Inconsistent literal block quoting.

... def __init__(self, value): ... self.value = value ... ... def __call__(self, other): ... return self.value + other

We 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)

System Message: ERROR/3 (<string>, line 43)

Inconsistent literal block quoting.

2

Additional 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.

Pickling

The library includes an alternative to the built-in pickle module (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 the marshal module to serialize code).

Author

This 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 notes

Partial 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 state

An 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 termed resolved. 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 class Resolved, 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)

If string is 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 invalidation

The 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 += string

This invokes the __radd__ method on the list object. Since string is 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 static count method 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 state

In 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 like list and set do 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

System Message: WARNING/2 (<string>, line 176)

Bullet list ends without a blank line; unexpected unindent.

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 from object. We may instantiate the instance by

System Message: WARNING/2 (<string>, line 181)

Bullet list ends without a blank line; unexpected unindent.

calling the __new__ constructor of object (passing the class), then restore the instance dictionary.

  • Either of the above that define the __slots__ property. Instead of

System Message: WARNING/2 (<string>, line 185)

Bullet list ends without a blank line; unexpected unindent.

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 evaluation

The exec statement and the eval function 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 calls

There 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

System Message: WARNING/2 (<string>, line 209)

Bullet list ends without a blank line; unexpected unindent.

result immediately.

  • If the function is known, but not the arguments, we include the

System Message: WARNING/2 (<string>, line 212)

Bullet list ends without a blank line; unexpected unindent.

function as a closure and process partial application on it.

To-Do
  • Star arguments and double-star keyword arguments.
  • Ability to mark a method as "static", e.g. using a decorator.

Changelog

0.2 (released 2009/07/29)

Features:

  • Added pickling capability (experimental), both as standalone

System Message: WARNING/2 (<string>, line 232)

Bullet list ends without a blank line; unexpected unindent.

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.

Subscribe to package updates

Last updated Jan 5th, 2011

Download Stats

Last month:1

What does the lock icon mean?

Builds marked with a lock icon are only available via PyPM to users with a current ActivePython Business Edition subscription.

Need custom builds or support?

ActivePython Enterprise Edition guarantees priority access to technical support, indemnification, expert consulting and quality-assured language builds.

Plan on re-distributing ActivePython?

Get re-distribution rights and eliminate legal risks with ActivePython OEM Edition.