Add flip #4
Comments
I will take this one. Please assign to me |
Ramdas flip reverses the order of the first two arguments of the function only. Should pyramda do the same or should it reverse all the arguments (similar to what lodash does) ? |
It should probably reverse only the first two arguments. "All the arguments" is not well defined when currying is involved IMO. It honestly might also make sense to drop support for keyword arguments in Pyramda. They just don't mix well with currying, and supporting them makes the implementation of currying much more complex. |
Yeah, I personally am not a huge fan of mixing currying with keyword args as well. def flip(f):
'''
Calls the function f by flipping the first two positional
arguments
'''
def wrapper(*args, **kwargs):
flipped = args[1::-1] + args[2::] if len(args) > 1 else args
return f(*flipped, **kwargs)
return wrapper With keyword arguments, I think if we preserve the order in which the arguments are passed, we could potentially make flip work with both positional and keyword arguments. Not very clean though I think |
That works. I think it would read nicer if a helper function was extracted to handle the list processing though: def flip_first_two(xs):
... return a list that's like xs with the first two items flipped ...
def flip(f):
def wrapper(*args, **kwargs):
return f(*flip_first_two(args), **kwargs)
return wrapper I'm not sure how this will cooperate with currying. If a three-argument function is passed to |
Yeah agree on helper function for list processing. The list slicing syntax is a bit cryptic and taking it out into a separate function does make for a cleaner read. Does the below code snippet address your second point about flip cooperating with currying? Or did I misunderstand your point about the three argument function? from pyramda import curry
@curry
def flip(f):
'''
Calls the function f by flipping the first two positional
arguments
'''
def flip_first_two(xs):
return xs[1::-1] + xs[2::] if len(xs) > 1 else xs
def wrapper(*args, **kwargs):
return f(*flip_first_two(args), **kwargs)
return wrapper
def merge_three(a, b, c):
return [a, b, c]
flip(merge_three)(1,2,3)
>> [2, 1, 3] |
The function returned by flip(merge_three)(1, 2, 3)
flip(merge_three)(1)(2)(3)
flip(merge_three)(1, 2)(3) And passing too many arguments should raise an error. I'm not sure how to properly curry the function returned by |
got it. I will give it a think as well |
Alright, I think I figured out how to do this properly. In def flip(f):
def wrapped(*args, **kwargs):
... call f with flipped args ...
f_spec = make_func_curry_spec(f)
return curry_by_spec(f_spec, wrapped) That ought to ensure the wrapper around |
Neat! I will tinker with it over the weekend |
@jackfirth , regarding your earlier point on using a higher order contract library to enforce that the input function to flip accepts atleast two arguments. Would it not be simpler just to inspect the argspec of the incoming function and raise an error if the number positional args is less than two: def flip(f):
def wrapped(*args, **kwargs):
... call f with flipped args ...
def f_does_not_take_atleast_two_args(f_spec):
return len(f_spec.arg_names) < 2
f_spec = make_func_curry_spec(f)
if f_does_not_take_atleast_two_args(f_spec):
raise TypeError('Function f should take atleast two args')
return curry_by_spec(f_spec, wrapped) In this way we dont create an external dependency on an external library. It may however make sense to use a external library if we are planning to enforce contracting across multiple functions in pyramda. What do you think? |
That would work for But a first pass at implementing |
Definitely agree on argument checking for better usability. |
Sounds great! I recommend looking at Sanctuary for guidance on contract checking. They had to solve this problem too and have very good error messages now. |
May behave weirdly with kwargs
The text was updated successfully, but these errors were encountered: