diff options
author | Ian Johnson <ian.johnson@appliedlanguage.com> | 2012-09-11 19:21:25 +0400 |
---|---|---|
committer | Ian Johnson <ian.johnson@appliedlanguage.com> | 2012-09-11 19:21:25 +0400 |
commit | 00a61feb61b21c823c41c601b08723a771194f32 (patch) | |
tree | 65e75a048d3fe26b67d173658f550d60e513e6a6 | |
parent | a648c962cbd20c6bff948b6ae716aba03e8a252d (diff) |
Kleisli choice arrow added.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/pypeline/core/arrows/function_arrow.py | 1 | ||||
-rw-r--r-- | src/pypeline/core/arrows/kleisli_arrow.py | 1 | ||||
-rw-r--r-- | src/pypeline/core/arrows/kleisli_arrow_choice.py | 65 | ||||
-rw-r--r-- | src/pypeline/core/arrows/tests/kleisli_arrow_choice_test.py | 175 |
5 files changed, 243 insertions, 0 deletions
@@ -1,4 +1,5 @@ *.py[co~] +*.md~ # Packages *.egg 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) |