Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ R.add(date(1,2,3), date(1,2,3)) # float('nan)
- [ ] bind
- [ ] both
- [ ] call
- [ ] chain
- [x] chain
- [ ] clamp
- [x] 0.1.2 clone

Expand Down
1 change: 1 addition & 0 deletions ramda/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .ap import ap
from .append import append
from .binary import binary
from .chain import chain
from .clone import clone
from .comparator import comparator
from .compose import compose
Expand Down
17 changes: 17 additions & 0 deletions ramda/chain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from .map import map
from .private._curry2 import _curry2
from .private._dispatchable import _dispatchable
from .private._isFunction import _isFunction
from .private._makeFlat import _makeFlat
from .private._xchain import _xchain


def inner_chain(fn, monad):
if _isFunction(monad):
def wrapper(x):
return fn(monad(x))(x)
return wrapper
return _makeFlat(False)(map(fn, monad))


chain = _curry2(_dispatchable(['fantasy-land/chain', 'chain'], _xchain, inner_chain))
30 changes: 30 additions & 0 deletions ramda/private/_flatCat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from ._forceReduced import _forceReduced
from ._helper import getAttribute
from ._isArrayLike import _isArrayLike
from ._xArrayReduce import _xArrayReduce
from ._xfBase import XfBase
from ._xReduce import _xReduce


class XPreservingReduced(XfBase):
def __init__(self, xf):
self.xf = xf

def step(self, result, _input):
ret = getAttribute(self.xf, '@@transducer/step')(result, _input)
if getAttribute(ret, '@@transducer/reduced'):
return _forceReduced(ret)
return ret


class XFlatCat(XfBase):
def __init__(self, xf):
self.xf = XPreservingReduced(xf)

def step(self, result, _input):
if not _isArrayLike(_input):
return _xArrayReduce(self.xf, result, [_input])
return _xReduce(self.xf, result, _input)


def _flatCat(xf): return XFlatCat(xf)
5 changes: 5 additions & 0 deletions ramda/private/_forceReduced.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def _forceReduced(x):
return {
'@@transducer/value': x,
'@@transducer/reduced': True
}
6 changes: 6 additions & 0 deletions ramda/private/_xchain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from ._flatCat import _flatCat
from ._xmap import _xmap


def _xchain(f):
return lambda xf: _xmap(f)(_flatCat(xf))
96 changes: 96 additions & 0 deletions test/test_chain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

import unittest

import ramda as R
from ramda.private._isTransformer import _isTransformer

from .helpers.Id import Id
from .helpers.listXf import listXf

"""
https://github.com/ramda/ramda/blob/master/test/chain.js
"""

intoArray = R.into([])
def add1(x): return [x + 1]
def dec(x): return [x - 1]
def times2(x): return [x * 2]


class TestChain(unittest.TestCase):
def test_maps_a_function_over_a_nested_list_and_returns_the_shallow_flattened_result(self):
self.assertEqual([2, 4, 6, 2, 0, 20, -6, 10, 14], R.chain(times2, [1, 2, 3, 1, 0, 10, -3, 5, 7]))
self.assertEqual([2, 4, 6], R.chain(times2, [1, 2, 3]))

def test_does_not_flatten_recursively(self):
def f(xs):
if len(xs) == 0:
return []
return [xs[0]]

self.assertEqual([1, [2], 3], R.chain(f, [[1], [[2], 100], [], [3, [4]]]))

def test_maps_a_function_into_a_shallow_flat_result(self):
self.assertEqual([2, 4, 6, 8], intoArray(R.chain(times2), [1, 2, 3, 4]))

def test_XFlatCat_if_input_is_not_arrayLike(self):
self.assertEqual([2, 4, 6, 8], intoArray(R.chain(R.multiply(2)), [1, 2, 3, 4]))

def test_interprets_r_as_a_monad(self):
def h(r): return r * 2
def f(a): return lambda r: r + a
bound = R.chain(f, h)
# (>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)
# h >>= f = \w -> f (h w) w
self.assertEqual((10 * 2) + 10, bound(10))
self.assertEqual([1, 2, 3, 1], R.chain(R.append, R.head)([1, 2, 3]))

def test_dispatches_to_objects_that_implement_chain(self):
class Obj:
def __init__(self, x):
self.x = x

def chain(self, f):
return f(self.x)

obj = Obj(100)
self.assertEqual([101], R.chain(add1, obj))

def test_dispatches_to_transformer_objects(self):
self.assertEqual(True, _isTransformer(R.chain(add1, listXf)))

def test_xFlatCat_if_input_not_arrayLike(self):
add1ListXf = R.chain(add1, listXf)

def test_composes(self):
mdouble = R.chain(times2)
mdec = R.chain(dec)
self.assertEqual([19, 39, 59], mdec(mdouble([10, 20, 30])))

def test_can_compose_transducer_style(self):
mdouble = R.chain(times2)
mdec = R.chain(dec)
xcomp = R.compose(mdec, mdouble)
self.assertEqual([18, 38, 58], intoArray(xcomp, [10, 20, 30]))

def test_fantasy_land(self):
id = R.chain(add1, Id(10))
self.assertEqual([11], id)

def test_forcedReduced(self):
def reducedStep(acc, x):
if x > 4:
return R.reduced(acc + x)
return acc + x
reducedListXf = {
'@@transducer/init': lambda: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'@@transducer/step': reducedStep,
'@@transducer/result': lambda x: x
}
times2ReducedListXf = R.chain(times2, reducedListXf)
self.assertEqual((1 + 2 + 3) * 2, R.reduce(times2ReducedListXf, 0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
self.assertEqual((1 + 2) * 2, R.reduce(times2ReducedListXf, 0, [1, 2]))


if __name__ == '__main__':
unittest.main()