"""Shorthand syntax for making thunks.

Use _ as a placeholder in an expression to create a one-argument
function that fills in the placeholder and evaluates the expression.
For example:

    >>> from thunk import _
    >>> numbers = [3, 8, 4, 1, 2]
    >>> filter(_ < 5, numbers)
    [3, 4, 1, 2]
    >>> map(_ + 7, numbers)
    [10, 15, 11, 8, 9]
    >>> words = 'lovely spam and eggs'.split()
    >>> filter(_.endswith_('s'), words)
    ['eggs']
    >>> 

Notice that when calling a method on _ you need to append an
underscore to the method name (in order to distinguish a method
call from an attribute lookup.

The illusion is far from complete, but it's a fun hack."""

__author__ = 'Ka-Ping Yee'
__date__ = '2007-02-07'

def identity(x):
    return x

class CallThunk:
    def __init__(self, thunk, name):
        self.thunk = thunk
        self.name = name

    def __call__(self, *args, **kw):
        name = self.name
        def thunk(target):
            return getattr(target, name)(*args, **kw)
        return Thunk(thunk)

class Thunk:
    def __init__(self, thunk=identity):
        self.thunk = thunk

    def __call__(self, target):
        return self.thunk(target)

    def __lt__(self, other):
        def thunk(target):
            return target < other
        return Thunk(thunk)

    def __le__(self, other):
        def thunk(target):
            return target <= other
        return Thunk(thunk)

    def __gt__(self, other):
        def thunk(target):
            return target > other
        return Thunk(thunk)

    def __ge__(self, other):
        def thunk(target):
            return target >= other
        return Thunk(thunk)

    def __eq__(self, other):
        def thunk(target):
            return target == other
        return Thunk(thunk)

    def __ne__(self, other):
        def thunk(target):
            return target != other
        return Thunk(thunk)

    def __add__(self, other):
        def thunk(target):
            return target + other
        return Thunk(thunk)

    def __sub__(self, other):
        def thunk(target):
            return target - other
        return Thunk(thunk)

    def __mul__(self, other):
        def thunk(target):
            return target * other
        return Thunk(thunk)

    def __div__(self, other):
        def thunk(target):
            return target / other
        return Thunk(thunk)

    def __getattr__(self, other):
        if other in ['__repr__', '__coerce__']:
            raise AttributeError(other)
        if other.endswith('_'): # method call
            return CallThunk(self, other[:-1])
        else: # get attribute
            def thunk(target):
                return getattr(target, other)
            return Thunk(thunk)

    def __getitem__(self, other):
        def thunk(target):
            return target[other]
        return Thunk(thunk)

_ = Thunk()
