Created
February 2, 2011 18:18
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
__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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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