Javascript has a poorly constructed but convenient "arguments" variable inside every function, such that you can pass arguments through a function like so:
function foo(a, b, c) {
return bar.apply(this, arguments);
}
function bar(a, b, c) {
return [a, b, c];
}
foo(2, 3, 5); // returns [2, 3, 5]
Is there an easy way to do a similar thing in Python?
>>> def foo(*args):
... return args
>>> foo(1,2,3)
(1,2,3)
is that what you want?
Yeah, this is what I should have said.
def foo(*args):
return bar(*args)
You don't need to declare the function with (a,b,c). bar(...) will get whatever foo(...) gets.
My other crummier answer is below:
I was so close to answering "No, it can't easily be done" but with a few extra lines, I think it can.
#cbrauchli great idea using locals(), but since locals() also returns local variables, if we do
def foo(a,b,c):
n = "foobar" # any code that declares local variables will affect locals()
return bar(**locals())
we'll be passing an unwanted 4th argument, n, to bar(a,b,c) and we'll get an error. To solve this, you'd want to do something like arguments = locals() in the very first line i.e.
def foo(a, b, c):
myargs = locals() # at this point, locals only has a,b,c
total = a + b + c # we can do what we like until the end
return bar(**myargs) # turn the dictionary of a,b,c into a keyword list using **
How about using * for argument expansion?
>>> def foo(*args):
... return bar(*(args[:3]))
>>> def bar(a, b, c):
... return [a, b, c]
>>> foo(1, 2, 3, 4)
[1, 2, 3]
I think this most closely resembles your javascript snippet. It doesn't require you to change the function definition.
>>> def foo(a, b, c):
... return bar(**locals())
...
>>> def bar(a, b, c):
... return [a, b, c]
...
>>> foo(2,3,5)
[2, 3, 5]
Note that locals() gets all of the local variables, so you should use it at the beginning of the method and make a copy of the dictionary it produces if you declare other variables. Or you can use the inspect module as explained in this SO post.
Related
I'm building a pipe with Ramda.js which accepts three arguments. The first function needs those three arguments, and it's result is used in the second function. However, the second function also needs one of the initial arguments. I cannot figure out the branching to build something like it.
In pseudocode style, I need something like this:
const composedFunction = R.pipe(
firstFunction,
secondFunction,
);
const firstFunction = (reusedArgument, secondArgument, thirdArgument) => someAnswer;
const secondFunction = (reusedArgument, someAnswer);
console.log(composedFunction({ foo: bar }, [5, 3, 4], [100, 12, 12]));
I can think of a few solutions:
Wrap your pipe inside another function so that functions in your composition can still refer to the original parameters.
Here func2 accepts the output of func1 but also has access to the initial b parameter. Obviously func2 must be curried and be designed to accept its "data" as the last parameter (which is a tenet of Ramda and functional programming in general I'd say).
const func3 = (a, b, c) =>
pipe(func1, func2(b))
(a, b, c);
func3(10, 20, 30);
Other option, func1 returns an array which you can destructure in func2.
I don't think this is particularly nice but it is an option:
const func1 = (a, b, c) => [a + c, b];
const func2 = ([sum, b]) => sum * b;
const func3 = pipe(func1, func2);
func3(10, 20, 30);
I think the simplest thing here is to not bother with Ramda's pipe function, which is not designed to handle such case, and just write it manually:
const func1 = (a, b, c) => `func1 (${a}, ${b}, ${c})`
const func2 = (a, d) => `func2 (${a}, ${d})`
const func3 = (a, b, c) => func2 (func1 (a, b, c), a)
console .log (func3 ('a', 'b', 'c'))
Ramda has recently been considering a way to make this easier for longer pipelines; even with that, though, the above is probably simpler for just a few functions.
Say I have a function with three arguments, 2 of them having a default value:
function f(a, b=2, c=3){
console.log(a, b, c)
}
If I'm perfectly happy with b's value, is there a way to call f and specify a value for c directly?
In Python I'd do something like f(1, c=5)?
Use cases: if I don't know b's default value or if there are many more arguments it would be cumbersome not to be able to do such a thing
is there a way to call f and specify a value for c directly?
No, JavaScript doesn't have that kind of named parameter.
You can call f using the default for b by giving undefined:
f(1, undefined, 5);
function f(a, b = 2, c = 3){
console.log(a, b, c)
}
f(1, undefined, 5);
Alternately, you can define f differently: Have it accept an object that it destructures into parameters, then call it with an object:
function f({a, b = 2, c = 3}){
console.log(a, b, c)
}
f({a: 1, c: 5});
If you want it to be valid to call it with no object (it's an error with the above), provide a default for the destructured parameter:
function f({a, b = 2, c = 3} = {}){
// -------------------------^^^^^
console.log(a, b, c)
}
You could pass an object with named keys like so:
function f({a, b=2, c=3}){
console.log(a, b, c)
}
f({b: 4})
In python I can pass a dict whose keys match parameters' names with the ** (double-splat) operator:
def foo(a, b):
print (a - b)
args = {'b': 7, 'a': 10}
foo(**args) # prints 3
How to do the same in ES6? This doesn't work:
function foo(a, b) {
console.log(a - b)
}
args = {b: 7, a: 10}
foo(...args)
NB: I'm looking for a solution that wouldn't involve changing the signature of foo because I want it to be used either way (with and without destructuring). So the following should work:
foo(<magic>args);
foo(123, 456);
Bonus question: why is the error message "undefined is not a function"? What exactly is undefined here?
(As answered by #Nina Scholz in the comments, this is because ... requires its argument to have Symbol.iterator, which is not defined for objects).
How to do the same in ES6?
There are no named arguments in JS, only positional ones. So the answer is: you can not.
What you can do is either emulate named arguments via object passing, as #Andy suggested.
function foo({ a, b }) {
console.log(a - b);
}
let args = { b: 7, a: 10 };
foo(args);
Or you could make args to be an array, so you can destruct it into positional arguments.
function foo(a, b) {
console.log(a - b);
}
let args = [10, 7];
foo(...args);
Okay-okay, just for the sake of the argument: it is possible to write a function that will extract parameters of foo and yield properties of args in required order.
function * yolo(args, fn) {
const names = fn.toString().match(/\(.+\)/)[0]
.slice(1, -1).split(',')
.map(x => x.trim());
while (names.length) {
yield args[names.shift()];
}
}
function foo(a, b) {
console.log(a - b);
}
const args = { b: 7, a: 10 };
foo(...yolo(args, foo));
I would not dare to use it in production though.
You need to wrap your args in curly braces, and again in the argument list for the function.
function foo({a, b}) {
console.log(a - b)
}
let args = {b: 7, a: 10}
foo({...args})
Given the following:
var average = R.lift(R.divide)(R.sum, R.length)
How come this works as a pointfree implementation of average? I don't understand why I can pass R.sum and R.length when they are functions and therefore, I cannot map the lifted R.divide over the functions R.sum and R.length unlike in the following example:
var sum3 = R.curry(function(a, b, c) {return a + b + c;});
R.lift(sum3)(xs)(ys)(zs)
In the above case the values in xs, ys and zs are summed in a non deterministic context, in which case the lifted function is applied to the values in the given computational context.
Expounding further, I understand that applying a lifted function is like using R.ap consecutively to each argument. Both lines evaluate to the same output:
R.ap(R.ap(R.ap([tern], [1, 2, 3]), [2, 4, 6]), [3, 6, 8])
R.lift(tern)([1, 2, 3], [2, 4, 6], [3, 6, 8])
Checking the documentation it says:
"lifts" a function of arity > 1 so that it may "map over" a list, Function or other object that satisfies the FantasyLand Apply spec.
And that doesn't seem like a very useful description at least for me. I'm trying to build an intuition regarding the usage of lift. I hope someone can provide that.
The first cool thing is that a -> b can support map. Yes, functions are functors!
Let's consider the type of map:
map :: Functor f => (b -> c) -> f b -> f c
Let's replace Functor f => f with Array to give us a concrete type:
map :: (b -> c) -> Array b -> Array c
Let's replace Functor f => f with Maybe this time:
map :: (b -> c) -> Maybe b -> Maybe c
The correlation is clear. Let's replace Functor f => f with Either a, to test a binary type:
map :: (b -> c) -> Either a b -> Either a c
We often represent the type of a function from a to b as a -> b, but that's really just sugar for Function a b. Let's use the long form and replace Either in the signature above with Function:
map :: (b -> c) -> Function a b -> Function a c
So, mapping over a function gives us a function which will apply the b -> c function to the original function's return value. We could rewrite the signature using the a -> b sugar:
map :: (b -> c) -> (a -> b) -> (a -> c)
Notice anything? What is the type of compose?
compose :: (b -> c) -> (a -> b) -> a -> c
So compose is just map specialized to the Function type!
The second cool thing is that a -> b can support ap. Functions are also applicative functors! These are known as Applys in the Fantasy Land spec.
Let's consider the type of ap:
ap :: Apply f => f (b -> c) -> f b -> f c
Let's replace Apply f => f with Array:
ap :: Array (b -> c) -> Array b -> Array c
Now, with Either a:
ap :: Either a (b -> c) -> Either a b -> Either a c
Now, with Function a:
ap :: Function a (b -> c) -> Function a b -> Function a c
What is Function a (b -> c)? It's a bit confusing because we're mixing the two styles, but it's a function that takes a value of type a and returns a function from b to c. Let's rewrite using the a -> b style:
ap :: (a -> b -> c) -> (a -> b) -> (a -> c)
Any type which supports map and ap can be "lifted". Let's take a look at lift2:
lift2 :: Apply f => (b -> c -> d) -> f b -> f c -> f d
Remember that Function a satisfies the requirements of Apply, so we can replace Apply f => f with Function a:
lift2 :: (b -> c -> d) -> Function a b -> Function a c -> Function a d
Which is more clearly written:
lift2 :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)
Let's revisit your initial expression:
// average :: Number -> Number
const average = lift2(divide, sum, length);
What does average([6, 7, 8]) do? The a ([6, 7, 8]) is given to the a -> b function (sum), producing a b (21). The a is also given to the a -> c function (length), producing a c (3). Now that we have a b and a c we can feed them to the b -> c -> d function (divide) to produce a d (7), which is the final result.
So, because the Function type can support map and ap, we get converge at no cost (via lift, lift2, and lift3). I'd actually like to remove converge from Ramda as it isn't necessary.
Note that I intentionally avoided using R.lift in this answer. It has a meaningless type signature and complex implementation due to the decision to support functions of any arity. Sanctuary's arity-specific lifting functions, on the other hand, have clear type signatures and trivial implementations.
As I have hard time understanding the same issue, I decided to take a peek from Ramda's source code. Will write a blogpost about this in the future. Meanwhile—I made a commented gist how Ramda's lift work step by step.
from: https://gist.github.com/philipyoungg/a0ab1efff1a9a4e486802a8fb0145d9e
// Let's make an example function that takes an object and return itself.
// 1. Ramda's lift level
lift(zipObj)(keys, values)({a: 1}) // returns {a: 1}
// this is how lift works in the background
module.exports = _curry2(function liftN(arity, fn) {
var lifted = curryN(arity, fn);
return curryN(arity, function() {
return _reduce(ap, map(lifted, arguments[0]), Array.prototype.slice.call(arguments, 1)); // found it. let's convert no 1 to no 2
});
});
// 2. Ramda's reduce level
reduce(ap, map(zipObj, keys))([values])
// first argument is the function, second argument is initial value, and the last one is lists of arguments. If you don't understand how reduce works, there's a plenty of resources on the internet
// 3. Ramda's ap level
ap(map(zipObj, keys), values)
// how ap works in the background
module.exports = _curry2(function ap(applicative, fn) {
return (
typeof applicative.ap === 'function' ?
applicative.ap(fn) :
typeof applicative === 'function' ? //
function(x) { return applicative(x)(fn(x)); } : // because the first argument is a function, ap return this.
// else
_reduce(function(acc, f) { return _concat(acc, map(f, fn)); }, [], applicative)
);
});
// 4. Voilà. Here's the final result.
map(zipObj, keys)({a: 1})(values({a: 1}))
// Hope it helps you and everyone else!
I've written a CoffeeScript function that resembles this contrived example:
my_func = (a, b, use_args = false) ->
if use_args?
other_func 'foo', a, b, 'bar'
else
other_func 'foo', 'bar'
This compiles to the following JavaScript:
var my_func;
my_func = function(a, b, use_args) {
if (use_args == null) {
use_args = false;
}
if (use_args != null) {
return other_func('foo', a, b, 'bar');
} else {
return other_func('foo', 'bar');
}
};
Is there a DRY approach to this function that would eliminate the duplicate call to other_func? Something like:
my_func = (a, b, use_args = false) ->
other_func 'foo', a if use_args?, b if use_args?, 'bar'
but that's actually syntactically correct? Hopefully I'm not missing something obvious here. I'm not sure if CoffeeScript provides a handy way to do this, or if there's just a better JavaScript pattern I should be using.
Incidentally, I can't modify other_func to use different parameters, since it's actually _gaq.push(), part of the Google Analytics library that adds tracking information to a queue.
First off, I think the code in your original question is fine (other than the = false in the argument list—see my comment). It's perfectly efficient and readable. But if the repetition really bothers you, read on.
Chris is on the right track in his answer. Since this is CoffeeScript, there are some syntactic sugars you can take advantage of:
Instead of arr.splice(1, 0, [a, b]), you can write arr[1...1] = [a, b].
Instead of func.apply(null, arr), you can simply write func arr....
So, combining those, you can get your function down to 3 short, repetition-free lines:
my_func = (a, b, use_args) ->
args = [foo, bar]
args[1...1] = [a, b] if use_args
other_func args...
Notice that you don't have to do a use_args? check; if it's null or undefined, it'll be coerced to false automatically (by JavaScript) for if use_args.
Use a combination of:
Array.splice() to inject the additional parameters in to an array of params, and
apply() to call the function with an array of parameters.
Here's a demo:
http://jsfiddle.net/ZSVtB/
var my_func = function(a, b, use_args) {
var args = []
args.push('foo')
use_args && args.push(a, b)
args.push('bar')
return other_func.apply(null, args)
}
Okay, no with from now on.