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:
authorIan Johnson <ian.johnson@appliedlanguage.com>2012-10-09 18:00:59 +0400
committerIan Johnson <ian.johnson@appliedlanguage.com>2012-10-09 18:00:59 +0400
commitf9c76d461c8fc1d4df81f7e82f0db6fbc9a956fa (patch)
tree0aaa34959bf8a5e3cab3e4b28f63161f3565dd96
parent918d347b6fed69176322eccc15b3bb560c2b7e3b (diff)
parentb91bbc0a303859390c22384b16f96893610690e1 (diff)
Merge branch 'master' of https://github.com/ianj-als/pypeline
-rw-r--r--.gitignore1
-rw-r--r--README.md9
-rw-r--r--src/pypeline/core/arrows/function_arrow.py1
-rw-r--r--src/pypeline/core/arrows/kleisli_arrow.py1
-rw-r--r--src/pypeline/core/arrows/kleisli_arrow_choice.py65
-rw-r--r--src/pypeline/core/arrows/tests/kleisli_arrow_choice_test.py175
6 files changed, 248 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 8ab72c5..5da0b3e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
*.py[co~]
+*.md~
# Packages
*.egg
diff --git a/README.md b/README.md
index c418a6e..beb71f3 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,9 @@ To view setup help:
This Python implementation provides the following arrows:
* Function arrow,
- * Function choice arrow, and
- * Kleisli arrow (It is expected that the Kleisli choice arrow will be implemented shortly).
+ * Function choice arrow,
+ * Kleisli arrow, and
+ * Kleisli choice arrow.
And also provides helper functions that lift the arrow abstraction to a *pipeline component* level, in order that pipelines can be constructed without "seeing" an arrow directly. However, if the programmer wishes, the underlying arrow classes can be used to build pipelines with or without the helper functions.
@@ -167,10 +168,10 @@ Returns a pipeline component that is the composition of two components with a wi
helpers.cons_composed_component(first_component, second_component)
-Returns a components that is the composition of the `first_component` and the `second_component`.
+Returns a component that is the composition of the `first_component` and the `second_component`.
#### Constructing a Parallel Component
helpers.cons_parallel_component(top_component, bottom_component)
-Returns a component that will execute the two provided components in parallel. The input to the constructed component is a pair, whose first value is applied to the `top_component` and the second value is applied to the `bottom_component`. The constructed component`s output shall be a pair, whose first value is the output of the top component, and the second value is the output of the bottom component.
+Returns a component that will execute the two provided components in "parallel". The input to the constructed component is a pair, whose first value is applied to the `top_component` and the second value is applied to the `bottom_component`. The constructed component`s output shall be a pair, whose first value is the output of the top component, and the second value is the output of the bottom component.
diff --git a/src/pypeline/core/arrows/function_arrow.py b/src/pypeline/core/arrows/function_arrow.py
index 5746429..17e282d 100644
--- a/src/pypeline/core/arrows/function_arrow.py
+++ b/src/pypeline/core/arrows/function_arrow.py
@@ -48,6 +48,7 @@ class FunctionArrow(Arrow):
type(func) is not types.MethodType:
raise ValueError("Must be a function or method")
+ Arrow.__init__(self)
self._func = func
#
diff --git a/src/pypeline/core/arrows/kleisli_arrow.py b/src/pypeline/core/arrows/kleisli_arrow.py
index aed24eb..344641a 100644
--- a/src/pypeline/core/arrows/kleisli_arrow.py
+++ b/src/pypeline/core/arrows/kleisli_arrow.py
@@ -40,6 +40,7 @@ class KleisliArrow(Arrow):
not isinstance(f, types.MethodType)):
raise ValueError("Function must be a function")
+ Arrow.__init__(self)
self._patcher = patcher
self._func = f
diff --git a/src/pypeline/core/arrows/kleisli_arrow_choice.py b/src/pypeline/core/arrows/kleisli_arrow_choice.py
new file mode 100644
index 0000000..aba2d4e
--- /dev/null
+++ b/src/pypeline/core/arrows/kleisli_arrow_choice.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/>.
+#
+from pypeline.core.arrows.arrow import Arrow, ArrowChoice
+from pypeline.core.arrows.kleisli_arrow import KleisliArrow
+from pypeline.core.types.either import *
+
+
+class KleisliArrowChoice(ArrowChoice, KleisliArrow):
+ def __init__(self, patcher, f):
+ ArrowChoice.__init__(self)
+ KleisliArrow.__init__(self, patcher, f)
+
+ # left :: a b c -> a (Either b d) (Either c d)
+ def left(self):
+ def left_func(either):
+ if isinstance(either, Left):
+ return self._func(either.val) >= (lambda a: self._patcher(Left(a)))
+ elif isinstance(either, Right):
+ return self._patcher(either)
+ else:
+ raise ValueError("Must be of type Either")
+
+ return KleisliArrowChoice(self._patcher, left_func)
+
+ # right :: a b c -> a (Either d b) (Either d c)
+ def right(self):
+ def right_func(either):
+ if isinstance(either, Right):
+ return self._func(either.val) >= (lambda a: self._patcher(Right(a)))
+ elif isinstance(either, Left):
+ return self._patcher(either)
+ else:
+ raise ValueError("Must be of type Either")
+
+ return KleisliArrowChoice(self._patcher, right_func)
+
+ # (+++) :: a b c -> a b' c' -> a (Either b b') (Either c c')
+ def __add__(self, other):
+ if not isinstance(other, KleisliArrowChoice):
+ raise ValueError("Must be a KleisliArrowChoice")
+
+ return self.left() >> other.right()
+
+ # (|||) :: a b d -> a c d -> a (Either b c) d
+ def __or__(self, other):
+ if not isinstance(other, KleisliArrowChoice):
+ raise ValueError("Must be a KleisliArrowChoice")
+
+ return (self + other) >> KleisliArrow(other._patcher, lambda either: self._patcher(either.val))
diff --git a/src/pypeline/core/arrows/tests/kleisli_arrow_choice_test.py b/src/pypeline/core/arrows/tests/kleisli_arrow_choice_test.py
new file mode 100644
index 0000000..0678720
--- /dev/null
+++ b/src/pypeline/core/arrows/tests/kleisli_arrow_choice_test.py
@@ -0,0 +1,175 @@
+#
+# 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
+
+from pypeline.core.arrows.function_arrow import FunctionArrow
+from pypeline.core.arrows.kleisli_arrow_choice import KleisliArrowChoice
+from pypeline.core.arrows.kleisli_arrow import KleisliArrow, split as kleisli_split, unsplit as kleisli_unsplit
+from pypeline.core.types.either import Left, Right
+from pypeline.core.types.just import Just, return_ as just_return
+from pypeline.core.types.state import State, return_ as state_return
+
+
+class KleisliArrowChoiceUnitTest(unittest.TestCase):
+ def test_left_with_maybe_monad(self):
+ value = 9
+ f = lambda x: x * 9
+ k = KleisliArrowChoice(just_return, lambda a: Just(f(a))).left()
+
+ left = KleisliArrow.runKleisli(k, Left(value))
+ self.assertEquals(Just(Left(f(value))), left)
+
+ right = KleisliArrow.runKleisli(k, Right(value))
+ self.assertEquals(Just(Right(value)), right)
+
+
+ def test_left_with_state_monad(self):
+ s1 = "*2"
+ w = lambda a: a * 2
+ f = lambda a: State(lambda s: (w(a), s.append(s1) or s))
+ k = KleisliArrowChoice(state_return, f).left()
+
+ value = 3.141
+ left_state = KleisliArrow.runKleisli(k, Left(value))
+ left = State.runState(left_state, [])
+ left_target = (Left(w(value)), [s1])
+ self.assertEquals(left_target, left)
+
+ right_state = KleisliArrow.runKleisli(k, Right(value))
+ right = State.runState(right_state, [])
+ right_target = (Right(value), [])
+ self.assertEquals(right_target, right)
+
+
+ def test_right_with_maybe_monad(self):
+ value = 9
+ f = lambda x: x * 9
+ k = KleisliArrowChoice(just_return, lambda a: Just(f(a))).right()
+
+ left = KleisliArrow.runKleisli(k, Left(value))
+ self.assertEquals(Just(Left(value)), left)
+
+ right = KleisliArrow.runKleisli(k, Right(value))
+ self.assertEquals(Just(Right(f(value))), right)
+
+
+ def test_right_with_state_monad(self):
+ s1 = "*2"
+ w = lambda a: a * 2
+ f = lambda a: State(lambda s: (w(a), s.append(s1) or s))
+ k = KleisliArrowChoice(state_return, f).right()
+
+ value = 3.141
+ left_state = KleisliArrow.runKleisli(k, Left(value))
+ left = State.runState(left_state, [])
+ left_target = (Left(value), [])
+ self.assertEquals(left_target, left)
+
+ right_state = KleisliArrow.runKleisli(k, Right(value))
+ right = State.runState(right_state, [])
+ right_target = (Right(w(value)), [s1])
+ self.assertEquals(right_target, right)
+
+
+ def test_triple_add_with_maybe_monad(self):
+ value = 11
+ f = lambda x: x * 9
+ k1 = KleisliArrowChoice(just_return, lambda a: Just(f(a)))
+
+ h = lambda x: x - 9
+ k2 = KleisliArrowChoice(just_return, lambda a: Just(h(a)))
+
+ arrow = k1 + k2
+
+ left = KleisliArrow.runKleisli(arrow, Left(value))
+ left_target = Just(Left(f(value)))
+ self.assertEquals(left_target, left)
+
+ right = KleisliArrow.runKleisli(arrow, Right(value))
+ right_target = Just(Right(h(value)))
+ self.assertEquals(right_target, right)
+
+
+ def test_triple_add_with_state_monad(self):
+ s1 = "*2"
+ w = lambda a: a * 2
+ f = lambda a: State(lambda s: (w(a), s.append(s1) or s))
+ k1 = KleisliArrowChoice(state_return, f)
+
+ s2 = "-9"
+ y = lambda a: a - 9
+ h = lambda a: State(lambda s: (y(a), s.append(s2) or s))
+ k2 = KleisliArrowChoice(state_return, h)
+
+ arrow = k1 + k2
+
+ value = 19
+ left_state = KleisliArrow.runKleisli(arrow, Left(value))
+ left = State.runState(left_state, [])
+ left_target = (Left(w(value)), [s1])
+ self.assertEquals(left_target, left)
+
+ right_state = KleisliArrow.runKleisli(arrow, Right(value))
+ right = State.runState(right_state, [])
+ right_target = (Right(y(value)), [s2])
+ self.assertEquals(right_target, right)
+
+
+ def test_triple_pipe_with_maybe_monad(self):
+ value = 11
+ f = lambda x: x * 9
+ k1 = KleisliArrowChoice(just_return, lambda a: Just(f(a)))
+
+ h = lambda x: x - 9
+ k2 = KleisliArrowChoice(just_return, lambda a: Just(h(a)))
+
+ arrow = k1 | k2
+
+ left = KleisliArrow.runKleisli(arrow, Left(value))
+ left_target = Just(f(value))
+ self.assertEquals(left_target, left)
+
+ right = KleisliArrow.runKleisli(arrow, Right(value))
+ right_target = Just(h(value))
+ self.assertEquals(right_target, right)
+
+
+ def test_triple_pipe_with_state_monad(self):
+ s1 = "*2"
+ w = lambda a: a * 2
+ f = lambda a: State(lambda s: (w(a), s.append(s1) or s))
+ k1 = KleisliArrowChoice(state_return, f)
+
+ s2 = "-9"
+ y = lambda a: a - 9
+ h = lambda a: State(lambda s: (y(a), s.append(s2) or s))
+ k2 = KleisliArrowChoice(state_return, h)
+
+ arrow = k1 | k2
+
+ value = 19
+ left_state = KleisliArrow.runKleisli(arrow, Left(value))
+ left = State.runState(left_state, [])
+ left_target = (w(value), [s1])
+ self.assertEquals(left_target, left)
+
+ right_state = KleisliArrow.runKleisli(arrow, Right(value))
+ right = State.runState(right_state, [])
+ right_target = (y(value), [s2])
+ self.assertEquals(right_target, right)