slavaganzin / ramda.py Goto Github PK
View Code? Open in Web Editor NEWPython clone of Ramda.js
License: MIT License
Python clone of Ramda.js
License: MIT License
The flip
method doesn't reverse the argument's order of a partially applied function. The following example demonstrates the issue; both add
and multiply
are partially applied reduce
. The functions are still expecting an accumulator and a list of values.
Flipping the partially applied reduce
should expect a list of values and an accumulator.
Currently, this doesn't happen which results in an error TypeError: 'int' object is not iterable
.
import ramda as R
# add :: Number → [Number] → Number
add = R.reduce(lambda a, b: a + b)
# multiply :: Number → [Number] → Number
multiply = R.reduce(lambda a, b: a * b)
total = R.compose(
R.flip(multiply)([1, 2, 3]),
R.flip(add)([1, 2, 3])
)(0)
assert total == 36
Using the Javascript library, it works as expected.
const assert = require('assert');
const R = require('ramda');
// add :: Number → [Number] → Number
const add = R.reduce((a, b) => a + b);
// multiply :: Number → [Number] → Number
const multiply = R.reduce((a, b) => a * b);
const total = R.compose(
R.flip(multiply)([1, 2, 3]),
R.flip(add)([1, 2, 3])
)(0);
assert.strictEqual(total, 36);
What I see:
The following test case
def test_open_cursor(db_context):
class TestClass:
def one(self):
return 1
one = R.invoker(0, "one")(TestClass())
assert one == 1
fails with
arity = 0, f = 'one'
@curry
def invoker(arity, f):
"""Turns a named method with a specified arity into a function that can be
called directly supplied with arguments and a target object.
The returned function is curried and accepts arity + 1 parameters where
the final parameter is the target object"""
_args = ", ".join([f"x{i}" for i in range(0, arity)])
_f = f'lambda {_args}, object: getattr(object, "{f}")({_args})'
> return curry(eval(_f))
E File "<string>", line 1
E lambda , object: getattr(object, "one")()
E ^
E SyntaxError: invalid syntax
What I would expect:
R.invoker(0, 'method_name')(class_instance)
calls method_name
on class_instance
without any arguments.
The call to path_or
in the following example should return None
, but it returns 'blue'
path_or('blue', ["user", "preferred_color"])({'user': {'preferred_color': None}})
Hi,
I noticed that times
doesn't work as advertised in the RamdaJS docs. The RamdaJS version runs a function n times. The Python version returns a list from 1 to n-1.
RamdaJS:
R.times(R.identity, 5); //=> [0, 1, 2, 3, 4]
Python:
>>> from ramda import *
>>> times(identity, 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: times() takes 1 positional argument but 2 were given
>>> times(5)
[1, 2, 3, 4]
Thank you.
Hello,
It seems that lt
, lte
, gt
, gte
are reversed from their ramdajs counterparts.
In ramdajs:
R.lt(2, 1); // false
R.lte(2, 1); // false
R.gt(2, 1); // true
R.gte(2, 1); // true
In Python:
>>> lt(2,1)
True
>>> lte(2,1)
True
>>> gt(2,1)
False
>>> gte(2,1)
False
I wonder if this was an intentional decision? Because in Ramda one almost always wants to know if the second parameter is less than the first, for example. But it is inconsistent with ramdajs.
A related question: How can I use the R.__
placeholder function in Python?
R.lt(R.__, 1)(2); // false
PS. Sorry for all the issues I'm filing. I really appreciate all the great work you're doing in porting Ramda to Python!!
I know, this thing was an open an shut case in ramda.js (ramda/ramda#1317) and it would be much nicer to handle this kind of control flow with a Maybe
type. But since no such thin exists in python afaik, how about we sidestep and implement something like this anyway?
def raise(e):
def foo(*_):
raise(e)
return foo
I for sure would find it useful.
Cheers!
Hello,
I noticed that repeat isn't curried.
RamdaJS:
R.repeat(" ")(3) // [" ", " ", " "]
Python:
>>> from ramda import *
>>> repeat(" ")(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: repeat() missing 1 required positional argument: 'times'
Thank you.
Hello,
I noticed that group_with(equals)
raises an exception when given an empty string.
>>> group_with(equals)('AABBB')
['AA', 'BBB']
>>> group_with(equals)('')
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
group_with(equals)('')
File "C:\Python36\lib\site-packages\toolz\functoolz.py", line 303, in __call__
return self._partial(*args, **kwargs)
File "C:\Python36\lib\site-packages\ramda\group_with.py", line 11, in group_with
group = [xs[0]]
IndexError: string index out of range
However, in ramdajs, an empty list is returned.
R.groupWith(R.equals)('AABBB') // ["AA", "BBB"]
R.groupWith(R.equals)('') // []
Thank you.
Hi, I think there is a bug in group_by
: acc[key]
is not initialized so you cannot append x to it. Probably acc
should be a defaultdict
that defaults to a list.
@curry
def group_by(f, xs):
acc = {}
for x in xs:
key = f(x)
try:
acc[key].append(x)
except KeyError:
acc[key] = [x]
return acc
Hello, very nice project, one little question would be how does it differ from jackfirth/pyramda's implementation?
ramda version 0.6.0
When I try to work with none field value in source object I get such exception:
TypeError: 'NoneType' object is not subscriptable
Example source:
import ramda as R
conc={'_id': '605c5122de4f9070960f2b0e', 'deviceIndex': 0, 'concentrator': {'type': 'MK-01', 'number': '48376021016855'}, 'commissioningData': None}
print (R.path_or([], ['commissioningData', 'channelsField'], conc))
Output is
Traceback (most recent call last):
File "/home/kss/tmp/py/ramda_test.py", line 9, in
print (R.path_or([], ['commissioningData', 'channelsField'], conc))
File "/usr/lib/python3.9/site-packages/toolz/functoolz.py", line 303, in call
return self._partial(*args, **kwargs)
File "/usr/lib/python3.9/site-packages/ramda/path_or.py", line 11, in path_or
return default_to(default, _path(path, value))
File "/usr/lib/python3.9/site-packages/toolz/functoolz.py", line 303, in call
return self._partial(*args, **kwargs)
File "/usr/lib/python3.9/site-packages/ramda/path.py", line 11, in path
current_value = current_value[key]
TypeError: 'NoneType' object is not subscriptable
In JavaScript Ramda all works well
const R=require('ramda')
let conc={'_id': '605c5122de4f9070960f2b0e', 'deviceIndex': 0, 'concentrator': {'type': 'MK-01', 'number': '48376021016855'}, 'commissioningData': null}
console.log (R.pathOr([], ['commissioningData', 'channelsField'], conc))
Output is []
Hello,
I noticed that try_catch
doesn't seem to evaluate the catcher function. For example, this is the behavior in ramdajs:
R.tryCatch(() => { throw 'foo'}, R.always('caught'))('bar') // "caught"
And here's the behavior in Python:
>>> def raise_(ex):
raise ex
>>> try_catch(lambda: raise_(Exception('foo')), always('caught'))('bar')
<function always at 0x0000027CA97EB158>
As you can see, the catcher function is returned, not evaluated. But what's really strange is that the catcher function keeps getting returned over and over when I try to evaluate it:
>>> catcher = try_catch(lambda: raise_(Exception('foo')), always('caught'))('bar')
>>> catcher
<function always at 0x0000027CA97EC510>
>>> catcher('bar') # Expected 'caught' here
<function always at 0x0000027CA97EC378>
>>> catcher('bar')('bar') # And here
<function always at 0x0000027CA97EC1E0>
Thank you.
def test_use_with_with_lambda_function():
assert use_with(lambda a, b: a + b, [identity, identity])(1, 2) == 3
fails because a lambda function does not have a proper __name__
attribute.
When calling
** Expected behaviour **
xs = [(-1, '23456789')]
post = take_last_while(lambda i: i[0] == 0, xs)
assert post == []
** Current behaviour **
xs = [(-1, '23456789')]
post = take_last_while(lambda i: i[0] == 0, xs)
assert post == [(-1, '23456789')]
Hi There,
Shall we add types to all these functions. Python might not be able to support all the cases we have in ramda but most of them should be possible.
What do you think?
Python 3.7 spits this warning:
python3.7/site-packages/ramda/flatten.py:1: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working from collections import Iterable
Hello and thank you for porting ramdajs to Python!
I'd like to report an inconsistency between ramdajs's pipe
and yours. In ramdajs, pipe's leftmost function can accept more than one argument. The documentation says: "The first argument may have any arity; the remaining arguments must be unary." For example, this works in ramdajs:
R.pipe(R.add, R.negate)(1, 2); // -3
However, in Python, the same code fails:
>>> from ramda import *
>>> pipe(add, negate)(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes 1 positional argument but 2 were given
My workaround is to wrap the leftmost function in apply
. But it's not exactly the same, because you have to put the arguments into a list.
>>> pipe(apply(add), negate)([1, 2])
-3
Your compose
has the same issue, but with the rightmost function.
Thank you.
inspect.getfullargspec()
should be used instead of inspect.getargspec()
to prevent DeprecationWarning in Python 3.7:
python3.7/site-packages/ramda/private/curry_spec/make_func_curry_spec.py:19: DeprecationWarning: inspect.getargspec() is deprecated since Python 3.0, use inspect.signature() or inspect.getfullargspec()
Hi! Nice lib, I've been using for a little while.
I found this bug with map
which doesn't behave the same as I'd expect from Ramda.js, perhaps there's something I'm missing?
print(
pipe(
map(lambda value: value + 1),
)([1, 2, 3, 4, 5, 6, 7, 8, 9]),
)
This gives me the error:
Traceback (most recent call last):
map(lambda value: value + 1),
TypeError: map() must have at least two arguments.
Hi, I found this problem
ramda.aperture(2, [1, 2, 3, 4, 5, 6])
should return [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]]
but it returns [[1, 2], [2, 3], [3, 4], [4, 5], [6]]
Hello! 👋
Our company uses ramda as part of our stack and dependabot notified us that you published a new version for ramda just recently.
We noticed that you changed your versioning from semver to date of publishing. Why did you make that choice?
For us end users it removes some of the explicitness in the nature and scope of changes introduced, and requires a better readout of the commits & changeset. It's never really a bad thing to better understand your deps, but you know how it goes; can't be going to read the code like that for all our deps ;)
Could you please move back to semver?
Thank you!
Hello,
I noticed that adjust
doesn't work as expected. From the ramdajs docs:
R.adjust(1, R.toUpper, ['a', 'b', 'c', 'd']); //=> ['a', 'B', 'c', 'd']
R.adjust(-1, R.toUpper, ['a', 'b', 'c', 'd']); //=> ['a', 'b', 'c', 'D']
In Python:
>>> adjust(1, to_upper, ['a', 'b', 'c', 'd'])
['a', 'b', 'c', 'd']
>>> adjust(-1, to_upper, ['a', 'b', 'c', 'd'])
['a', 'b', 'c', 'd']
Also, the negative index version of update
doesn't work as expected. From the ramdajs docs:
R.update(1, '_', ['a', 'b', 'c']); //=> ['a', '_', 'c']
R.update(-1, '_', ['a', 'b', 'c']); //=> ['a', 'b', '_']
In Python:
>>> update(1, '_', ['a', 'b', 'c'])
['a', '_', 'c']
>>> update(-1, '_', ['a', 'b', 'c'])
['a', 'b', 'c']
Thank you.
The following test case is failing:
def test_use_with():
def add(x, y):
return x + y
two = R.use_with(R.add, [lambda x: x, R.identity])(1, 1)
assert two == 2
probably because the current implementation of use_with
is expecting named functions as transformers.
This wouldn't be so bad if not for e.g. pipe
returning a lambda function that cannot be used as a transformer.
PS: @slavaGanzin coming back to python after a year in javascript land where I learned to love fp in general and ramda in particular, I am incredibly grateful for the work you have done here!
Nice work for making this Gem, @slavaGanzin!
Found a little issue with path_or
. Not sure if this is a bug or not.
R.path_or(42, [0, 1], [None])
In [84]: R.path_or(42, [0, 1], a)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-84-80b69f40b48b> in <module>
----> 1 R.path_or(42, [0, 1], a)
xx/python3.8/site-packages/toolz/functoolz.py in __call__(self, *args, **kwargs)
301 def __call__(self, *args, **kwargs):
302 try:
--> 303 return self._partial(*args, **kwargs)
304 except TypeError as exc:
305 if self._should_curry(args, kwargs, exc):
xx/python3.8/site-packages/ramda/path_or.py in path_or(default, path, value)
9 value at that path. Otherwise returns the provided default value"""
10 try:
---> 11 return default_to(default, _path(path, value))
12 except (KeyError, IndexError):
13 return default
xx/python3.8/site-packages/toolz/functoolz.py in __call__(self, *args, **kwargs)
301 def __call__(self, *args, **kwargs):
302 try:
--> 303 return self._partial(*args, **kwargs)
304 except TypeError as exc:
305 if self._should_curry(args, kwargs, exc):
xx/python3.8/site-packages/ramda/path.py in path(keys, dict)
9 current_value = dict
10 for key in keys:
---> 11 current_value = current_value[key]
12 return current_value
TypeError: 'NoneType' object is not subscriptable
For RamdaJs, the result is expected:
R.pathOr(42, [0, 1], [null]); // => 42
Do you think this issue could be fixed by add TypeError
to below L12
?
Lines 6 to 13 in 2fd6bdc
CI seems to be not working because some config is missing.
As github actions are working very well as a CI provider for projects hosted on github (and are completely free for open source projects) I would suggest, we set it up. Also, If you agree, I would volunteer to do that. Let me know what you think.
Kind Regards
For example:
assert (
R.map(
R.apply_spec(
{
"foo": R.prop("bar"),
}
),
[{"bar": 1}, {"bar": 2}],
)
== [{"foo": 1}, {"foo": 2}]
)
fails because it returns [{"foo": 1}, {"foo": 1}]
@curry
def map(f, xs):
if isinstance(xs, collections.Mapping):
return dict([(k, f(v)) for k, v in xs.items()])
return [f(x) for x in xs]
AttributeError: module 'collections' has no attribute 'Mapping'
ramda==0.6.0
This problem happens under the following circumstances:
prop_or
on a Python object that is not a dictionaryIn this case ramda will try to treat it as a dictionary and call o[name]
. In itself that is correct - because o
might be a dictionary - but the code should not crash if o
is not a dictionary.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.