Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ianj-als/pypeline.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--setup.py2
-rw-r--r--src/pypeline/__init__.py5
-rw-r--r--src/pypeline/core/arrows/function_arrow_choice.py2
-rw-r--r--src/pypeline/core/arrows/tests/function_arrow_tests.py95
-rw-r--r--src/pypeline/core/types/cont.py71
-rw-r--r--src/pypeline/core/types/just.py8
-rw-r--r--src/pypeline/core/types/monad.py3
-rw-r--r--src/pypeline/core/types/nothing.py12
-rw-r--r--src/pypeline/core/types/state.py18
-rw-r--r--src/pypeline/core/types/tests/cont_tests.py65
10 files changed, 232 insertions, 49 deletions
diff --git a/setup.py b/setup.py
index 641ce72..0b32104 100644
--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,7 @@ from setuptools import setup, find_packages
setup(
name = "pypeline",
- version = "0.2.7",
+ version = "0.3.0",
packages = find_packages("src", exclude = ["*tests"]),
package_dir = {'': 'src'},
diff --git a/src/pypeline/__init__.py b/src/pypeline/__init__.py
index 761ed8b..09c66e9 100644
--- a/src/pypeline/__init__.py
+++ b/src/pypeline/__init__.py
@@ -16,3 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pypeline. If not, see <http://www.gnu.org/licenses/>.
#
+import sys
+
+if sys.version_info < (2, 7):
+ raise Exception("Python version too old, please use 2.7 or newer.")
+
diff --git a/src/pypeline/core/arrows/function_arrow_choice.py b/src/pypeline/core/arrows/function_arrow_choice.py
index a90326b..7334f4b 100644
--- a/src/pypeline/core/arrows/function_arrow_choice.py
+++ b/src/pypeline/core/arrows/function_arrow_choice.py
@@ -114,7 +114,7 @@ def test(arrow):
#
# This maker returns an arrow that implements if
#
-# if_maker :: (b -> c) -> (b -> d) -> (b -> d) -> a b (Either d d)
+# if_maker :: (b -> c) -> (b -> d) -> (b -> d) -> a b d
#
def if_maker(predicate_func, then_func, else_func):
return test(FunctionArrow(predicate_func)) >> (FunctionArrowChoice(then_func) | FunctionArrowChoice(else_func))
diff --git a/src/pypeline/core/arrows/tests/function_arrow_tests.py b/src/pypeline/core/arrows/tests/function_arrow_tests.py
index c5184ba..b38e33c 100644
--- a/src/pypeline/core/arrows/tests/function_arrow_tests.py
+++ b/src/pypeline/core/arrows/tests/function_arrow_tests.py
@@ -26,7 +26,7 @@ class ArrowUnitTests(unittest.TestCase):
# Arrow law tests
#
- # Identity
+ # arr id = id
def test_identity(self):
iden = lambda x: x
arrow = FunctionArrow(iden)
@@ -48,6 +48,69 @@ class ArrowUnitTests(unittest.TestCase):
self.assertEquals(arrow_func_comp(value), arrow_comp(value))
+ # first (arr f) = arr (first f)
+ def first_arr_f_is_arr_first_f(self):
+ f = lambda x: x + 1
+ fst = lambda x: x[0] + 1
+
+ arrow_one = FunctionArrow(f).first()
+ arrow_two = FunctionArrow(fst)
+
+ value = (8, -10)
+ self.assertEquals(arrow_one(value), arrow_two(value))
+
+
+ # first (a >>> b) = first a >>> first b
+ def test_first_a_b_is_first_a_first_b(self):
+ a = lambda x: x * 9
+ b = lambda x: x - 7
+ c = lambda x: b(a(x))
+
+ arrow_one = FunctionArrow(c).first()
+ arrow_two = FunctionArrow(a).first() >> FunctionArrow(b).first()
+
+ value = (7, 21)
+ self.assertEquals(arrow_one(value), arrow_two(value))
+
+
+ # first f >>> arr fst = arr fst >>> f
+ def test_first_arrow_fst_is_arrow_fst_func(self):
+ fst = lambda x: x[0]
+ f = lambda x: x * -9
+
+ arrow_one = FunctionArrow(f).first() >> FunctionArrow(fst)
+ arrow_two = FunctionArrow(fst) >> FunctionArrow(f)
+
+ value = (-3, 19)
+ self.assertEquals(arrow_one(value), arrow_two(value))
+
+
+ # first f >>> arr (id *** g) = arr (id *** g) >>> first f
+ def first_f_arr_id_g_is_arr_id_g_first_f(self):
+ id = lambda x: x
+ f = lambda x: x * -3
+ g = lambda x: x - 9
+
+ arrow_one = FunctionArrow(f).first() >> (FunctionArrow(id) * FunctionArrow(g))
+ arrow_two = (FunctionArrow(id) * FunctionArrow(g)) >> FunctionArrow(f).first()
+
+ value = (-9, 8)
+ self.assertEquals(arrow_one(value), arrow_two(value))
+
+
+ # first (first f) >>> arr assoc = arr assoc >>> first f
+ # where assoc((a, b), c) = (a, (b, c))
+ def first_first_f_arr_assoc_is_arr_assoc_first_f(self):
+ assoc = lambda ab, c: (ab[0], (ab[1], c))
+ f = lambda x: x + 3
+
+ arrow_one = FunctionArrow(f).first().first() >> FunctionArrow(assoc)
+ arrow_two = FunctionArrow(assoc) >> FunctionArrow(f).first()
+
+ value = ((1, 2), 3)
+ self.assertEquals(arrow_one(value), arrow_two(value))
+
+
# arr id >>> a = a = a >>> arr id
def test_arrow_id_func_is_func_is_func_comp_arrow(self):
a = lambda x: x + 1
@@ -61,12 +124,6 @@ class ArrowUnitTests(unittest.TestCase):
self.assertEquals(arrow_two(value), a(value))
- # first a >>> arr pi_1 = arr pi_1 >>> a
- def test_first_arrow_pi_is_arrow_pi_func(self):
- # Not sure what pi_1 is in this law
- pass
-
-
# first a >>> arr (id x f) = arr (id x f) >>> first a
# first a >>> second f = second f >>> first a
def test_first_arr_id_f_is_arr_id_f_first(self):
@@ -80,30 +137,6 @@ class ArrowUnitTests(unittest.TestCase):
self.assertEquals(arrow_one(value), arrow_two(value))
- # first a >>> arr alpha = arr alpha >>> first (first a)
- def test_first_a_comp_arr_alpha_is_arr_alpha_first_first_a(self):
- # not sure what alpha is in this law
- pass
-
-
- # first (arr f) = arr (f x id)
- # first f = arr (first f)
- # first f = first f
-
-
- # first (a >>> b) = first a >>> first b
- def test_first_a_b_is_first_a_first_b(self):
- a = lambda x: x * 9
- b = lambda x: x - 7
- c = lambda x: b(a(x))
-
- arrow_one = FunctionArrow(c).first()
- arrow_two = FunctionArrow(a).first() >> FunctionArrow(b).first()
-
- value = (7, 21)
- self.assertEquals(arrow_one(value), arrow_two(value))
-
-
#
# Ad hoc tests
#
diff --git a/src/pypeline/core/types/cont.py b/src/pypeline/core/types/cont.py
new file mode 100644
index 0000000..91614f5
--- /dev/null
+++ b/src/pypeline/core/types/cont.py
@@ -0,0 +1,71 @@
+#
+# Copyright Applied Language Solutions 2012
+#
+# This file is part of Pypeline.
+#
+# Pypeline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pypeline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pypeline. If not, see <http://www.gnu.org/licenses/>.
+#
+import types
+
+from pypeline.core.types.monad import Monad
+
+
+# Continuation monad
+class Cont(Monad):
+ def __init__(self, a):
+ super(Monad, self).__init__()
+ if type(a) is not types.FunctionType and \
+ type(a) is not types.MethodType:
+ raise ValueError("Must be a function or method")
+
+ self._cont = a
+
+ @staticmethod
+ def return_(a):
+ return Cont(a)
+
+ # Bind operator
+ # (>>=) :: m a -> (a -> m b) -> m b
+ def __ge__(self, f):
+ if type(f) is not types.FunctionType and \
+ type(f) is not types.MethodType:
+ raise ValueError("Must be a function or method")
+
+ return Cont(lambda k: Cont.runCont(self, lambda a: Cont.runCont(f(a), k)))
+
+ # Run a continuation
+ # runCont :: Cont r a -> (a -> r) -> r
+ @staticmethod
+ def runCont(c, f):
+ return c._cont(f)
+
+
+# return_ :: a -> Cont r a
+def return_(a):
+ return Cont.return_(lambda k: k(a))
+
+
+# Call current continuation
+# callCC :: ((a -> Cont r b) -> Cont r a) -> Cont r a
+def callCC(f):
+ if type(f) is not types.FunctionType and \
+ type(f) is not types.MethodType:
+ raise ValueError("Must be a function or method")
+
+ def function(k):
+ def function_arg(a):
+ return Cont(lambda x: k(a))
+ return Cont.runCont(f(function_arg), k)
+
+ return Cont(function)
diff --git a/src/pypeline/core/types/just.py b/src/pypeline/core/types/just.py
index 07d3c23..be84da4 100644
--- a/src/pypeline/core/types/just.py
+++ b/src/pypeline/core/types/just.py
@@ -26,14 +26,16 @@ from pypeline.core.types.monad import Maybe
#
class Just(Maybe):
def __init__(self, a):
+ super(Maybe, self).__init__()
if a is None:
raise ValueError("Value cannot be None")
self._a = a
# return
# return :: a -> m a
- def return_(self, a):
- return return_(a)
+ @staticmethod
+ def return_(a):
+ return Just(a)
def __ge__(self, function):
if type(function) is not types.FunctionType and \
@@ -65,4 +67,4 @@ class Just(Maybe):
def return_(a):
- return Just(a)
+ return Just.return_(a)
diff --git a/src/pypeline/core/types/monad.py b/src/pypeline/core/types/monad.py
index a6ff269..62c9781 100644
--- a/src/pypeline/core/types/monad.py
+++ b/src/pypeline/core/types/monad.py
@@ -44,5 +44,6 @@ class Monad(object):
# Erm, maybe...
#
class Maybe(Monad):
- pass
+ def __init__(self):
+ super(Monad, self).__init__()
diff --git a/src/pypeline/core/types/nothing.py b/src/pypeline/core/types/nothing.py
index 9d6e7e2..a31c8b7 100644
--- a/src/pypeline/core/types/nothing.py
+++ b/src/pypeline/core/types/nothing.py
@@ -30,8 +30,12 @@ class Nothing(Maybe):
cls._instance = super(Nothing, cls).__new__(cls, *args, **kwargs)
return cls._instance
- def return_(self, a):
- return return_(a)
+ def __init__(self, *a):
+ pass
+
+ @staticmethod
+ def return_(a):
+ return Nothing()
def __eq__(self, other):
return Nothing._instance is other
@@ -49,5 +53,5 @@ class Nothing(Maybe):
return False
-def return_(a):
- return Nothing()
+def return_(*a):
+ return Nothing.return_(a)
diff --git a/src/pypeline/core/types/state.py b/src/pypeline/core/types/state.py
index 2c47e15..1a2c390 100644
--- a/src/pypeline/core/types/state.py
+++ b/src/pypeline/core/types/state.py
@@ -35,16 +35,18 @@ from pypeline.core.types.monad import Monad
#
class State(Monad):
def __init__(self, a):
- if type(a) is types.FunctionType or \
- type(a) is types.MethodType:
- self._func = a
- else:
- self._func = lambda s: (a, s)
+ super(Monad, self).__init__()
+ if type(a) is not types.FunctionType and \
+ type(a) is not types.MethodType:
+ raise ValueError("Must be a function or method")
+
+ self._func = a
# return
# return :: a -> m a
- def return_(self, a):
- return return_(a)
+ @staticmethod
+ def return_(a):
+ return State(lambda s: (a, s))
# (>>=) :: State s a -> (a -> State s b) -> State s b
# (State h) >>= f = State $ \s -> let (a, newState) = h s
@@ -83,4 +85,4 @@ class State(Monad):
def return_(a):
- return State(a)
+ return State.return_(a)
diff --git a/src/pypeline/core/types/tests/cont_tests.py b/src/pypeline/core/types/tests/cont_tests.py
new file mode 100644
index 0000000..13ad05f
--- /dev/null
+++ b/src/pypeline/core/types/tests/cont_tests.py
@@ -0,0 +1,65 @@
+#
+# Copyright Applied Language Solutions 2012
+#
+# This file is part of Pypeline.
+#
+# Pypeline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pypeline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pypeline. If not, see <http://www.gnu.org/licenses/>.
+#
+import unittest
+import os
+import sys
+
+from pypeline.core.types.cont import Cont, return_, callCC
+
+
+class ContMonadUnitTest(unittest.TestCase):
+ def test_single_value(self):
+ value = 7
+ cont = return_(value)
+ result = Cont.runCont(cont, lambda x: x)
+ self.assertEquals(value, result)
+
+
+ def test_bind(self):
+ square_cps = lambda x: return_(x ** 2)
+ add_three_cps = lambda x: return_(x + 3)
+ result = Cont.runCont(square_cps(4) >= add_three_cps, lambda x: x)
+ self.assertEquals(19, result)
+
+
+ def test_desugared_do_notation(self):
+ # do x_squared <- square_cont x
+ # y_squared <- square_cont y
+ # sum_of_squares <- add_cont x_squared y_squared
+ # return sum_of_squares
+ add_cps = lambda x, y: return_(x + y)
+ square_cps = lambda x: return_(x ** 2)
+ pythagoras_cps = lambda x, y: (square_cps(x) >=
+ ((lambda x_squared: square_cps(y) >=
+ ((lambda y_squared: add_cps(x_squared, y_squared) >=
+ (lambda sum_of_squares: return_(sum_of_squares)))))))
+ result = Cont.runCont(pythagoras_cps(3, 4), lambda x: x)
+ self.assertEquals(25, result)
+
+
+ def test_call_current_continuation(self):
+ # divide_cps :: Int -> Int -> (String -> Cont r Int) -> Cont r Int
+ def divide_cps(x, y, k):
+ return callCC(lambda ok: callCC(lambda not_ok: not_ok("Divide by zero error") if y is 0 else ok(x / y)) >=
+ (lambda err: k(err)))
+
+ error = lambda err: Cont(lambda _: sys.stderr.write(err + os.linesep))
+
+ self.assertEquals(3, Cont.runCont(divide_cps(10, 3, error), lambda x: x))
+ self.assertEquals(None, Cont.runCont(divide_cps(10, 0, error), lambda x: x))