Skip to content

mshroyer/pointfree

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introduction

pointfree is a small module that makes certain functional programming constructs more convenient to use in Python.

Specifically, it provides:

  • A decorator to enable automatic partial application of functions and methods.
  • Notations for function composition through operator overloading.
  • Helper functions to make composing generators more elegant.

The objective is to support the pointfree programming style in a lightweight and easy to use manner -- and in particular, to serve as a nice syntax for the kind of generator pipelines described in David Beazley's PyCon 2008 presentation, "Generator Tricks for Systems Programmers".

Examples

The pointfree module is about using function composition notation in conjunction with automatic partial application. Both of these features are achieved by wrapping functions in the pointfree class (which can also be applied as a decorator).

Several "pre-wrapped" helper functions are provided by the module. For instance, if you wanted to define a function that returns the sum of squares of the lengths of the strings in a list, you could do so by combining the helpers pfmap and pfreduce:

>>> from pointfree import *
>>> from operator import add

>>> fn = pfmap(len) >> pfmap(lambda n: n**2) >> pfreduce(add, initial=0)
>>> fn(["foo", "barr", "bazzz"])
50

Aside from the built-in helpers, you can define your own composable functions by applying pointfree as a decorator. Building upon an example from Beazley's presentation, suppose you have defined the following functions for operating on lines of text:

>>> import re

>>> @pointfree
... def gen_grep(pat, lines):
...     patc = re.compile(pat)
...     for line in lines:
...         if patc.search(line):
...             yield line

>>> @pointfree
... def gen_repeat(times, lines):
...     for line in lines:
...         for n in range(times):
...             yield line

>>> @pointfree
... def gen_upcase(lines):
...     for line in lines:
...         yield line.upper()

And you have some text too:

>>> bad_poetry = \
... """roses are red
... violets are blue
... I like generators
... and this isn't a poem
... um let's see...
... oh yeah and daffodils are flowers too""".split("\n")

Now say you want to find just the lines of your text that contain the name of a flower and print them, twice, in upper case. (A common problem, I'm sure.) The given functions can be combined to do so as follows, using pointfree's automatic partial application and its function composition operators:

>>> f = gen_grep(r'(roses|violets|daffodils)') \
...     >> gen_upcase \
...     >> gen_repeat(2) \
...     >> pfprint_all

>>> f(bad_poetry)
ROSES ARE RED
ROSES ARE RED
VIOLETS ARE BLUE
VIOLETS ARE BLUE
OH YEAH AND DAFFODILS ARE FLOWERS TOO
OH YEAH AND DAFFODILS ARE FLOWERS TOO

In addition to the >> operator for "forward" composition (borrowed from F#), functions can also be composed with the * operator, which is intended to be remniscent of the circle operator "∘" from algebra, or the corresponding dot operator in Haskell:

>>> @pointfree
... def f(x):
...     return x**2

>>> @pointfree
... def g(x):
...     return x+1

>>> h = f * g
>>> h(2)
9

Of course you don't have to define your methods using decorator notation in order to use pointfree; you can directly instantiate the class from an existing function or method:

>>> (pf(lambda x: x*2) * pf(lambda x: x+1))(3)
8

(pf is provided as a shorthand alias for the pointfree class.)

If you want automatic partial application but not the composition operators, use the module's partial decorator instead:

>>> @partial
... def add_three(a, b, c):
...     return a + b + c

>>> add_three(1)(2)(3)
6

The module's partial application support has some subtle intentional differences from normal Python function application rules. Please see the module reference for details.

Getting the module

Full documentation is available on the web at:

http://pointfree.readthedocs.org/en/latest/

The easiest way to install the latest release on your machine is to get it from PyPI using pip:

$ pip install pointfree

or easy_install:

$ easy_install pointfree

Or you can download the module manually and perform the standard distutils incantations:

$ tar xzf pointfree-*.tar.gz
$ cd pointfree-*
$ python setup.py install

The module's development repository is hosted on Github:

https://github.com/markshroyer/pointfree

and the very latest development version can also be installed using pip:

$ pip install git+git://github.com/markshroyer/pointfree.git

pointfree is compatible with the following Python implementations:

  • CPython 2.6, 2.7, 3.0, 3.1, 3.2, and 3.3
  • PyPy 1.9.0
  • IronPython 2.7.1

Python 3 is fully supported, including PEP 3102 keyword-only arguments.

About

Pointfree style support for Python

Resources

License

Stars

Watchers

Forks

Packages

No packages published