Skip to content

Instantly share code, notes, and snippets.

@shangaslammi
Created February 2, 2011 18:18
Show Gist options
  • Save shangaslammi/808116 to your computer and use it in GitHub Desktop.
Save shangaslammi/808116 to your computer and use it in GitHub Desktop.
Small prototype for playing around with functional concepts in Python (WORK IN PROGRESS)
__all__ = [
"functional",
"later",
"X", "Y", "Z", "IF"
]
import functools
import operator
later = object()
undefined = object()
class LambdaConstruct(object):
def __init__(self, f, pos, other=None):
self.f = f or (lambda x:x)
self.pos = pos
self.other = other
def __call__(self, *arg):
f = self.f
if len(arg) == 0:
return LambdaConstruct(lambda x:f(x)(), self.pos)
val = arg[self.pos]
other = self.other
if f is None:
result = val
elif other is None:
result = f(val)
else:
if isinstance(other, LambdaConstruct):
other = other(*arg)
result = f(val, other)
if isinstance(result, LambdaConstruct):
return result(*arg)
return result
def __lt__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x)<other, self.pos, other)
def __le__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x)<=other, self.pos, other)
def __gt__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x)>other, self.pos, other)
def __ge__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x)>=other, self.pos, other)
def __eq__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x)==other, self.pos, other)
def __ne__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x)!=other, self.pos, other)
def __add__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x) + other, self.pos,other)
def __radd__(self, other):
return LambdaConstruct(lambda x,other=other:other + self.f(x), self.pos,other)
def __sub__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x) - other, self.pos,other)
def __rsub__(self, other):
return LambdaConstruct(lambda x,other=other:other - self.f(x), self.pos,other)
def __mul__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x) * other, self.pos,other)
def __rmul__(self, other):
return LambdaConstruct(lambda x,other=other:other * self.f(x), self.pos,other)
def __div__(self, other):
return LambdaConstruct(lambda x,other=other:self.f(x) / other, self.pos,other)
def __rdiv__(self, other):
return LambdaConstruct(lambda x,other=other:other / self.f(x), self.pos,other)
def __neg__(self):
return LambdaConstruct(lambda x:-self.f(x), self.pos)
def __getattr__(self, name):
return LambdaConstruct(lambda x:getattr(self.f(x), name), self.pos)
def __getitem__(self, index):
return LambdaConstruct(lambda x:self.f(x)[index], self.pos)
def __str__(self):
return LambdaConstruct(str, self.pos)
def __unicode__(self):
return LambdaConstruct(unicode, self.pos)
def __len__(self):
return LambdaConstruct(len, self.pos)
def __nonzero__(self):
return LambdaConstruct(bool, self.pos)
def IF(cond, THEN=None, ELSE=None):
return LambdaConstruct(lambda x: THEN if cond(x) else ELSE, cond.pos)
class cons(object):
def __init__(self, head, tail):
self._head = head
self._tail = tail
def __iter__(self):
yield head
for i in self._tail: yield i
class functional(object):
def __init__(self, f):
self.f = f
def __mod__(self, args):
if not isinstance(args, tuple):
args = (args,)
if later in args:
return functional(LaterArgsPartial(self.f, args))
return functional(functools.partial(self.f, *args))
def __add__(self, f):
return functional(FunctionComposition(self.f, f))
def __ror__(self, other):
return self.f(other)
def __call__(_self, *_args, **_kw):
return _self.f(*_args, **_kw)
class FunctionComposition(object):
def __init__(self, f1, f2):
self.f1 = f1
self.f2 = f2
def __call__(_self, *_arg, **_kw):
return _self.f2(_self.f1(*_arg, **_kw))
class LaterArgsPartial(object):
def __init__(self, f, args):
self.f = f
self.args = args
def __call__(_self, *_args, **_kw):
args = list(_self.args)
supplied = list(_args)
for i,a in enumerate(args):
if not supplied: break
if a is later:
args[i] = supplied.pop(0)
if later in args:
return LaterArgsPartial(_self.f, args)
return _self.f(*args, **_kw)
X = LambdaConstruct(None, 0)
Y = LambdaConstruct(None, 1)
Z = LambdaConstruct(None, 2)
import unittest
from functional import *
class TestFunctional(unittest.TestCase):
def test_partial_application_1(self):
@functional
def add(a,b): return a+b
add5 = add % 5
self.assertEquals(add5(6), 11)
def test_partial_application_2(self):
@functional
def sum3(a,b,c): return a+b+c
add10 = sum3 % (3,7)
self.assertEquals(add10(5), 15)
def test_partial_application_later(self):
@functional
def sub(a,b): return a-b
sub3 = sub % (later, 3)
self.assertEquals(sub3(10), 7)
def test_function_composition(self):
@functional
def add(a,b): return a+b
add3 = add % 3
add7 = add % 7
add10 = add3 + add7
self.assertEquals(add10(8), 18)
def test_value_piping(self):
fmap = functional(map)
ffilter = functional(filter)
lt = functional(lambda x,y: y < x)
result = range(5) | (fmap % (lambda x:x*2)) | (ffilter % (lt % 5))
self.assertEquals(result, [0,2,4])
def test_x(self):
identity = X
self.assertEquals(identity(1), 1)
def test_x_lt(self):
lt5 = X < 5
self.assertTrue(lt5(4))
self.assertFalse(lt5(5))
self.assertFalse(lt5(6))
def test_x_lte(self):
lte10 = X <= 10
self.assertTrue(lte10(10))
self.assertTrue(lte10(9))
self.assertFalse(lte10(11))
def test_x_gt(self):
gt5 = X > 5
self.assertFalse(gt5(4))
self.assertFalse(gt5(5))
self.assertTrue(gt5(6))
def test_x_gte(self):
gte5 = X >= 5
self.assertFalse(gte5(4))
self.assertTrue(gte5(5))
self.assertTrue(gte5(6))
def test_x_eq(self):
eq5 = X == 5
self.assertFalse(eq5(4))
self.assertTrue(eq5(5))
self.assertFalse(eq5(6))
def test_x_neq(self):
neq5 = X != 5
self.assertTrue(neq5(4))
self.assertFalse(neq5(5))
self.assertTrue(neq5(6))
def test_x_plus(self):
add5 = X + 5
self.assertEquals(add5(8), 13)
add5 = 5 + X
self.assertEquals(add5(8), 13)
def test_x_minus(self):
sub5 = X - 5
self.assertEquals(sub5(7), 2)
sub_from5 = 5 - X
self.assertEquals(sub_from5(7), -2)
def test_x_mult(self):
mul3 = X * 3
self.assertEquals(mul3(5), 15)
mul3 = 3 * X
self.assertEquals(mul3(5), 15)
def test_x_div(self):
div2 = X / 2
self.assertEquals(div2(10), 5)
div10by = 10 / X
self.assertEquals(div10by(2), 5)
def test_x_combined_arithmetics(self):
calc = (X + 2) * (X + 3)
self.assertEquals(calc(1), 12)
self.assertEquals(calc(2), 20)
def test_x_method_map(self):
l = ["foo", "bar"]
m = map(X.upper(), l)
self.assertEquals(m, ["FOO", "BAR"])
def test_x_item(self):
first = X[0]
second = X[1]
self.assertEquals(first([1,2]), 1)
self.assertEquals(second([1,2]), 2)
def test_x_x(self):
dbl = X + X
self.assertEquals(dbl(6), 12)
def test_x_y(self):
add = (X + 2) + Y
self.assertEquals(add(3,6), 11)
def test_x_y_minus(self):
sub = X - (Y + 2)
self.assertEquals(sub(2,7), -7)
def test_x_y_z(self):
calc = X*2 + Y - Z
self.assertEquals(calc(1,2,3), 1)
def test_x_if(self):
abs_ = IF(X<0, THEN=-X, ELSE=X)
self.assertEquals(abs_(5), 5)
self.assertEquals(abs_(-5), 5)
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment