New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
new function: recall #2987
base: master
Are you sure you want to change the base?
new function: recall #2987
Conversation
@@ -177,6 +177,7 @@ export { default as propOr } from './propOr'; | |||
export { default as propSatisfies } from './propSatisfies'; | |||
export { default as props } from './props'; | |||
export { default as range } from './range'; | |||
export { default as recall } from './recall'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain the choice of the name "recall?" To me that mostly has to do with remembering things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm terrible at naming. The idea is that it calls, then re-calls, etc. collapse
also works.
* | ||
* @symb R.recall(f, a, b) = f(a)(b) | ||
*/ | ||
var recall = uncurryN(2, function(fn) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm extremely loathe to add new variadic functions to Ramda.
I would much rather deal with an API such as this:
recall (f) (a, b) //=> f (a) (b)
(Even that probably isn't possible. I'm quite certain that we will need to supply the final arity. But that's a different comment.)
I know that the generated function is going to be polyadic. That's the whole point. And it's not even as though it always returns functions of a specific arity. But I'm hoping to make pipe
, compose
and other leftover variadic functions into fixed-arity ones and not add new ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel the same way, I was just mirroring call
. The list of values can be passed as an array instead, as in apply
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the call
API was an early mistake as we felt our way around this space.
But I wouldn't want the arguments to be wrapped in an array. I was just thinking that this function would have to be a two-pass call: recall
takes a curried function and returns a function. You call that with the n
arguments you would have supplied one at a time to the original:
const foo = (a) => (b) => (c) => a + b + c
const bar = recall (foo)
bar (1, 2, 3) => 6
(Or some equivalent of this that accepts an arity instead of depending on the function's arity.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to call a function with a set of values in the fashion of call
and apply
. The difference here is there is no notion of arity since the n-order function is composed of unary functions. The list of values supplied doesn't represent an arity of a function, rather a serial list of singular inputs fed to the n-order function. So returning a function defeats the purpose in which case I'd rather just use uncurryN
somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps then we're at cross purposes. I was thinking that the goal was a to bring behavior similar to uncurryN
to work with Ramda-styles curried functions. I thought we were mostly just removing the word "manually" from the docs.
I'm not sure what your goal is hers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally uncurryN
would be made to work but I'd still want this functionality, otherwise I'd have just submitted a patch. Is anyone looking into it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. I've been pulled into something else, and haven't had any real free time for Ramda in the past week. If no one does it before then, I will try to get to it this weekend. (You can do it yourself. Any conversations of importance about this would be in the issues here; so a GitHub search should probably show anything of importance that isn't already captured in the source or the tests.)
*/ | ||
var recall = uncurryN(2, function(fn) { | ||
return function() { | ||
var numArgs = arguments.length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm quite sure that we need to supply the arity here as we do with uncurryN
.
What happens if our target output is a function? What if we want to convert
const makeHandler = (a) => (b) => (evt) => ({do: 'something', with: 'a, b, and evt'})
so that it can be used as
something .addHandler (recall (makeHandler, 42, false) )
where addHandler
accepts a function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good use. The value returned can be whatever it needs to be, including just another function. If we supply a function a => b => c => d => a + b * c + d
and want a function a particular depth, we just provide values up to that depth. For example, [ 2, 3 ]
if we want the function c => d => 5 * c + d
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I misread before and didn't notice && i < numArgs
. My initial reading suggested that this use case wouldn't be possible with that implementation.
it('accepts no more than N arguments for an N-order function', function() { | ||
eq(R.recall(fn, 1, 2, 3), R.recall(fn, 1, 2, 3, 4)); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel that this tests proves what it claims to prove. Perhaps with an innermost function in fn
that has an optional parameter and acts differently based on its presence.
var fn = function(a) {
var x = a + 1;
return function(b) {
var y = x * b;
return function(c, d = 0) {
return y * c + d;
};
};
};
where
fn (1) (2) (3) //=> 12
fn (1) (2) (3, 4) //=> 16
recall (fn, 1, 2, 3, 4) //=> 12
But then that makes me wonder if this is the correct behavior. It seems to me that if we do this as :: Int -> (a -> b -> ... -> n -> x) -> ((a, b, ..., n) -> x)
, then we could write this so that
recall (3, fn) (1, 2, 3, 4) //~> fn (1) (2) (3, 4)
which feels more in keeping with other Ramda functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's intended to support a special class of high-order function, those composed of unary functions. E.g. const f = g => h => v
, where f
, g
, and h
are unary functions. In compose
, all functions must be unary except the last because the inputs to the last function are outside the workings of compose
. Similarily, the last value of the high-order unary function can be anything, including a polyadic function, because it's considered to be the value and outside the workings of recall
. The tests themselves could use some work though, I agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I was responding to the test case. I don't think it really demonstrated that it "accepts no more than N arguments for an N-order function". But I am curious about which behavior we actually want. I'm thinking that it might be a little different from what's in this implementation.
Thank your for the PR. I'm afraid I forgot to do the research into why Did you look into updating |
@CrossEye No problem. I gave a cursory look into |
This is in reference to #2980. It supports a particular class of function: A unary function that if returns a function, is also unary with this same rule.