I'm trying to make middleware function lets call it debug, which should take some parameters, log them and pass to the next function:
const debug = (...args) => {
console.log(...args)
return args // will return array, not argument type variable
}
const compose = (...fns) => (...arg) => (
fns.slice().reverse().reduce(
(acc, fn) => !acc ? fn(...arg) : fn(acc), null
)
)
const f = x => x * x
const g = (a, b) => a + b
const makeMagic = compose(
f,
g,
debug
)
makeMagic(1, 2)
If I remove debug from composition everything works as expected, as soon as I place it at end, it breaks. Because it takes arguments but returns array.
I tried to rewrite debug this way:
function debug() {
console.info(arguments)
return arguments
}
But no way it fails.
A function always has only a single result value. You can't write a function that accepts multiple arguments and "returns" multiple values without some kind of container around them (like an array), it's just not a feature JavaScript has (nor do most other programming languages).
You can make debug a pure pass-through for a single argument, but not multiple. So for instance, you could make this work:
const result = debug(makeMagic(1, 2))
...where result would be the value makeMagic returned (after debug logs it), but you can't include it in the composed makeMagic the way you're trying to.
To have debug (or any other function) in the middle of a chain with your compose, you'd have to use a convention with it and all composeable functions about how they return multiple values, probably by having them all return an array. That also helps with the fact taht right now, your compose function has what I assume is a bug: It uses ...args any time a previous function returned a falsy value (0, "", NaN, null, undefined, or false), where I'm fairly sure you meant to do that only on the first call.
Here's a version of compose that expects composable functions, which are functions that return an array of their results:
const debug = (...args) => {
console.log(...args);
return args;
};
const compose = (...fns) => (...args) => (
fns.slice().reverse().reduce(
(acc, fn) => fn(...(acc || args)),
null
)
);
const f = x => [x * x];
const g = (a, b) => [a + b];
const makeMagic = compose(
f,
g,
debug
);
const result = makeMagic(1, 2);
console.log(result);
Related
Here's a 'compose' function which I need to improve:
const compose = (fns) => (...args) => fns.reduceRight((args, fn) => [fn(...args)], args)[0];
Here's a practical implementation of one:
const compose = (fns) => (...args) => fns.reduceRight((args, fn) => [fn(...args)], args)[0];
const fn = compose([
(x) => x - 8,
(x) => x ** 2,
(x, y) => (y > 0 ? x + 3 : x - 3),
]);
console.log(fn("3", 1)); // 1081
console.log(fn("3", -1)); // -8
And here's an improvement my mentor came to.
const compose = (fns) => (arg, ...restArgs) => fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
If we pass arguments list like that func(x, [y]) with first iteration, I still don't understand how do we make function work with unpacked array of [y]?
Let's analyse what the improved compose does
compose = (fns) =>
(arg, ...restArgs) =>
fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
When you feed compose with a number of functions, you get back... a function. In your case you give it a name, fn.
What does this fn function look like? By simple substitution you can think of it as this:
(arg, ...restArgs) => fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
where fns === [(x) => x - 8, (x) => x ** 2, (x, y) => (y > 0 ? x + 3 : x - 3)].
So you can feed this function fn with some arguments, that will be "pattern-matched" against (arg, ...restArgs); in your example, when you call fn("3", 1), arg is "3" and restArgs is [1] (so ...restArgs expands to just 1 after the comma, so you see that fn("3", 1) reduces to
fns.reduceRight((acc, func) => func(acc, 1), "3");
From this you see that
the rightmost function, (x, y) => (y > 0 ? x + 3 : x - 3) is called with the two arguments "3" (the initial value of acc) and 1,
the result will be passed as the first argument to the middle function with the following call to func,
and so on,
but the point is that the second argument to func, namely 1, is only used by the rightmost function, whereas it is passed to but ignored by the other two functions!
Conclusion
Function composition is a thing between unary functions.¹ Using it with functions with higher-than-1 arity leads to confusion.²
For instance consider these two functions
square = (x) => x**2; // unary
plus = (x,y) => x + y; // binary
can you compose them? Well, you can compose them into a function like this
sum_and_square = (x,y) => square(plus(x,y));
the compose function that you've got at the bottom of your question would go well:
sum_and_square = compose([square, plus]);
But what if your two functions were these?
apply_twice = (f) => ((x) => f(f(x))); // still unary, technically
plus = (x,y) => x + y; // still binary
Your compose would not work.
Even though, if the function plus was curried, e.g. if it was defined as
plus = (x) => (y) => x + y
then one could think of composing them in a function that acts like this:
f = (x,y) => apply_twice(plus(x))(y)
which would predictably produce f(3,4) === 10.
You can get it as f = compose([apply_twice, plus]).
A cosmetic improvement
Additionally, I would suggest a "cosmetic" change: make compose accept ...fns instead of fns,
compose = (...fns)/* I've only added the three dots on this line */ =>
(arg, ...restArgs) =>
fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
and you'll be able to call it without groupint the functions to be composed in an array, e.g. you'd write compose(apply_twice, plus) instead of compose([apply_twice, plus]).
Btw, there's lodash
There's two functions in that library that can handle function composition:
_.flow
_.flowRight, which is aliased to _.compose in lodash/fp
(¹) This is Haskell's choice (. is the composition operator in Haskell). If you apply f . g . h to more than one argument, the first argument will be passed thought the whole pipeline; that intermediate result will be applied to the second argument; that further intermediate result will be applied to the third argument, and so on. In other words, if you had haskellCompose in JavaScript, and if f was binary and g and h unary, haskellCompose(f, g, h)(x, y) would be equal to f(g(h(x)), y).
(²) Clojure's comp instead takes another choice. It saturates the rightmost function and then passes the result over to the others. So if you had clojureCompose in JavaScript, and f and g where unary while h binary, then clojureCompose(f, g, h)(x, y) would be equal to f(g(h(x,y))).
Might be because I'm used to Haskell's automatically curryed functions, but I prefer Haskell's choice.
I've been trying to learn the Ramda library and get my head around functional programming. This is mostly academic, but I was trying to create a nice logging function that I could use to log values to the console from within pipe or compose
The thing I noticed
Once you've curried a function with Ramda, invoking a function without any parameters returns the same function
f() returns f
but
f(undefined) and f(null)
do not.
I've created a utility function that brings these calls into alignment so that
f() equals f(null) even if f is curried.
// Returns true if x is a function
const isFunction = x =>
Object.prototype.toString.call(x) == '[object Function]';
// Converts a curried fuction so that it always takes at least one argument
const neverZeroArgs = fn => (...args) => {
let ret = args.length > 0 ?
fn(...args) :
fn(null)
return isFunction(ret) ?
neverZeroArgs(ret) :
ret
}
const minNullCurry = compose(neverZeroArgs, curry);
Here it is in use:
const logMsg = minNullCurry((msg, val) => {
if(isNil(msg) || msg.length < 1) console.log(val);
else if(isNil(val)) console.log(msg);
else console.log(`${msg}: `, val);
});
const logWithoutMsg = logMsg();
logWithoutMsg({Arrr: "Matey"})
Then if I want to use it in Ramda pipes or composition, I could do this:
// Same as logMsg, but always return the value you're given
const tapLog = pipe(logMsg, tap);
pipe(
prop('length'),
tapLog() // -> "5"
)([1,2,3,4,5]);
pipe(
prop('length'),
tapLog('I have an thing of length') // -> "I have an thing of length: 5"
)([1,2,3,4,5]);
pipe(
always(null),
tapLog('test') // -> "test"
)([1,2,3,4,5]);
I've just started with Ramda and was wondering if it comes with anything that might make this a bit easier/cleaner. I do realise that I could just do this:
const logMsg = msg => val => {
if(isNil(msg)) console.log(val);
else if(isNil(val)) console.log(msg);
else console.log(`${msg}: `, val);
});
and I'm done, but now I have to forever apply each argument 1 at a time.
Which is fine, but I'm here to learn if there are any fun alternatives. How can I transform a curried function so that f() returns f(null) or is it a code smell to even want to do that?
(Ramda founder and maintainer here).
Once you've curried a function with Ramda, invoking a function without any parameters returns the same function
f() returns f
but
f(undefined) and f(null)
do not.
Quite true. This is by design. In Ramda, for i < n, where n is the function length, calling a function with i arguments and then with j arguments should have the same behavior as if we'd called it originally with i + j arguments. There is no exception if i is zero. There has been some controversy about this over the years. The other co-founder disagreed with me on this, but our third collaborator agreed we me, and it's been like this ever since. And note that the other founder didn't want to treat it as though you'd supplied undefined/null, but to throw an error. There is a lot to be said for consistency.
I'm here to learn if there are any fun alternatives. How can I transform a curried function so that f() returns f(null) or is it a code smell to even want to do that?
It is not a code smell, not at all. Ramda does not supply this to you, and probably never will, as it doesn't really match the rest of the library. Ramda needs to be able to distinguish an empty call from one with a nil input, because for some users that might be important. But no one ever said that all your composition tools had to come from a particular library.
I see nothing wrong with what you've done.
If you are interested in a different API, something like this might possibly be interesting:
const {pipe, prop, always} = R
const tapLog = Object .assign (
(...val) => console .log (...val) || val,
{
msg: (msg) => (...val) => console .log (`${msg}:`, ...val) || val,
val: (...val) => (_) => console .log (...val) || _
}
)
tapLog ({Arrr: "Matey"})
pipe(
prop('length'),
tapLog // -> "5"
)([1,2,3,4,5]);
pipe(
prop('length'),
tapLog.msg('I have an thing of length') // -> "I have an thing of length: 5"
)([1,2,3,4,5]);
pipe(
always(null),
tapLog.val('test') // -> "test"
)([1,2,3,4,5]);
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
I am new to JavaScript and I am trying to write functional Programming to calculate if a number is Odd (isOdd) or not. I do not understand why I've got true and 0 or false and 1 when I called isOdd() twice in a row.
here is my code:
var mod = m => {return number => {return number % m }}
var eq = number1 => {return number2 => {return number1 === number2}}
var combine = (...fns) => { return arg => {for(let fn of fns.reverse()) arg = fn(arg); return arg;}}
var eq1 = eq(1)
var mod2 = mod(2)
var isOdd = combine(eq1, mod2)
mod2(4) -----> returns 0
mod2(3) -----> returns 1
eq1(1) -----> returns true
eq1(2) -----> returns false
isOdd(1) returns true
isOdd(1) returns 1 (what??)
I can not understand what I missed or what goes wrong. I tested this code in most browsers.
Appreciate if someone can explain in details.
I can wrap isOdd again and have something like
isOdd = (number) => {return Boolean(combine(eq1, mod2))}
that returns boolean value everytime. But I want to understand what I missed in the first place/
Array.prototype.reverse reverses the fns array in-place. You’re calling .reverse on fns every time you call isOdd, even though you pass a fixed argument list of functions in combine once. That list is scoped to combine and reversed on every call of isOdd(1).
In other words, your first isOdd(1) call remembers the fns array of your original combine(eq1, mod2) call. fns is [ eq1, mod2 ]. Then you call fns.reverse() to iterate over it; but this mutates fns to [ mod2, eq1 ]. You get the correct result, because you wanted to call the functions in reverse order. The resulting call is eq1(mod2(1)) === eq1(1) === true.
In your second isOdd(1) call, however, fns is still remembered, and it still has the reversed [ mod2, eq1 ] value, because its scope is combine, so another isOdd call doesn’t reset the originally passed fns. The second isOdd call reverses this array again because fns.reverse is called within isOdd. The resulting call is mod2(eq1(1)) === mod2(true) === 1 (because true % 2 is coerced to 1 % 2).
A working function would look like this:
const combine = (...fns) => {
const reversedFns = fns.reverse();
return (arg) => {
for(let fn of reversedFns){
arg = fn(arg);
}
return arg;
};
};
A simpler approach uses Array.prototype.reduceRight:
const combine = (...fns) => (arg) => fns.reduceRight((result, fn) => fn(result), arg);
Since you want to aggregate multiple operations onto a single result based on a list from right to left, reduceRight is the perfect method for this. You start with arg, go through the list of functions from right to left, then call the current function with arg, and that becomes the new arg for the next function. The final result is returned. All this is captured by the code fns.reduceRight((result, fn) => fn(result), arg).
I want to create a functions that returns the parameter of another function. I know I can use argument or rest operator to access parameter inside a function itself, but how can I get them outside that function?
const returnValue = (fn) => {
//How can I get the parameter of fn? Assume I know its arity.
}
For example:
const foo = 'bar'
const hello = 'world'
const returnFirstArgument = (val1, val2) => val1
const returnArgumentByPosition = (fn, num) => {
//Take fn as input, return one of its parameter by number
}
const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1) //Expect it returns 'world'
What you want isn't possible to do without modifying how returnFirstArgument behaves. Take for example the below piece of code:
const x = 1 + 2;
console.log(x); // 3
Before a value is assigned to x, the expression 1 + 2 needs to be evaluated to a value. In this case 1 + 2 gets evaluated to 3, so x gets assigned to 3, that way when we print it, it prints the literal number 3 out in the console. Since it is now just a number, we can't tell how 3 was derived (it could have come from 0 + 3, 1 * 3, etc...).
Now take a similar example below:
const max = Math.max(1, 2);
console.log(max); // 2
The same idea here applies from above. First Math.max(1, 2) is evaluated to the value of 2, which is then assigned to max. Again, we have no way of telling how 2 was derived.
Now consider a function:
const add = (x, y) => x + y;
const ans = add(1 + 2, Math.max(1, 2));
console.log(ans); // 5
When we call the function, the function's arguments are first evaluated to values. The parameters within the function are then assigned to copies of these values:
const ans = add(1 + 2, Math.max(1, 2));
// ^--------^------------- both evaluate first before function is invoked
so the above function call becomes:
const ans = add(3, 2);
As a result, inside the add function, x becomes 3 and y becomes 2. Just like with the above first two examples with variables, we have no way of knowing the 3 came from the expression 1+2 and that 2 came from the function call of Math.max(1, 2).
So, relating this back to your original question. Your function call is analogous to the add function call shown above:
const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1)
just like in the other examples, the arguments passed to the function can't be expressions, so they need to be evaluated first to values. returnFirstArgument(foo, hello) is evaluated to a value before the returnArgumentByPosition function is invoked. It will evaluate to the string "bar". This results in fn becoming "bar" inside of your returnArgumentByPosition. As "bar" is just a string, we again have to way of telling where it came from, and so, won't have access to the function which created it. As a result, we can't access the second argument of the function, since this information is not retained anywhere.
One approach to do what you're after is to create a recall function. The recall function is able to "save" the arguments you passed into it, and then expose them later. Put simply, it wraps your original function but is able to save the arguments and the result of calling your original function:
const recall = fn => (...args) => {
return {
args,
result: fn(...args),
}
};
const add = recall((x, y) => x + y);
const getN = ({args}, n) => {
return args[n];
}
const res = getN(add(1, 2), 1);
console.log(res);
The above approach means that add() will return an object. To get the result of calling add, you can use .result. The same idea applies to get the arguments of add(). You can use .args on the returned object. This way of saving data is fine, however, if you want a more functional approach, you can save the data as arguments to a function:
const recall = fn => (...args) => {
return selector => selector(
args, // arguments
fn(...args) // result
);
};
// Selectors
const args = args => args;
const result = (_, result) => result;
const getN = (wrapped, n) => {
return wrapped(args)[n];
}
const add = recall((x, y) => x + y);
const wrappedAns = add(1, 2);
const nth = getN(wrappedAns, 1);
console.log(nth); // the second argument
console.log(wrappedAns(result)); // result of 1 + 2
above, rather than returning an object like we were before, we're instead returning a function of the form:
return selector => selector(args, fn(...args));
here you can see that selector is a function itself which gets passed the arguments as well as the result of calling fn() (ie: your addition function). Above, I have defined two selector functions, one called args and another called result. If the selector above is the args function then it will be passed args as the first argument, which it then returns. Similarly, if the selector function above is the result function, it will get passed both the args and the result of calling fn, and will return the result the return value of fn(...args).
Tidying up the above (removing explicit returns etc) and applying it to your example we get the following:
const foo = 'bar';
const hello = 'world';
const recall = fn => (...args) => sel => sel(args, fn(...args));
const returnFirstArgument = recall((val1, val2) => val1);
const returnArgumentByPosition = (fn, num) => fn(x => x)[num];
const returnSecondArgument = returnArgumentByPosition(returnFirstArgument(foo, hello), 1);
console.log(returnSecondArgument); // world
Side note (for an approach using combinators):
In functional programming, there is a concept of combinators. Combinators are functions which can be used as a basis to form other (more useful) functions.
One combinator is the identity-function, which simply takes its first argument and returns it:
const I = x => x;
Another combinator is the K-combinator, which has the following structure:
const K = x => y => x;
You may have noticed that the first selector function args is missing an argument. This is because JavaScript doesn't require you to enter all the parameters that are passed as arguments into the function definition, instead, you can list only the ones you need. If we were to rewrite the args function so that it showed all the arguments that it takes, then it would have the following structure:
const args = (args, result) => args;
If we curry the arguments of this function, we get:
const args = args => result => args;
If you compare this function to the K-combinator above, it has the exact same shape. The K-combinator returns the first curried argument, and ignores the rest, the same applies with our args function. So, we can say that args = K.
Similarly, we can do a similar thing for the result selector shown above. First, we can curry the arguments of the results selector:
const result = _ => result => result;
Notice that this almost has the same shape as the K combinator, except that we're returning the second argument rather than the first. If we pass the identify function into the K-combinator like so K(I), we get the following:
const K = x => y => x;
K(I) returns y => I
As we know that I is x => x, we can rewrite the returned value of y => I in terms of x:
y => I
can be written as...
y => x => x;
We can then alpha-reduce (change the name of y to _ and x to result) to get _ => result => result. This now is the exact same result as the curried result function. Changing variable names like this is perfectly fine, as they still refer to the same thing once changed.
So, if we modify how selector is called in the recall function so that it is now curried, we can make use of the I and K combinators:
const I = x => x;
const K = x => y => x;
const recall = fn => (...args) => sel => sel(args)(fn(...args));
const args = K;
const result = K(I);
const getN = (fn, n) => fn(args)[n];
const add = recall((x, y) => x + y);
const addFn = add(1, 2);
const nth = getN(addFn, 1);
console.log(nth); // the second argument
console.log(addFn(result)); // result of 1 + 2
I've come across a pretty basic task for functional programming, I can't solve in javascript:
I'm trying wo write a (higher order) function that takes two arrays domain and image and returns a function. The returned function should return the n-th element of the image when given that n-th element of the domain:
Example:
let f = makeFunction([a,b,c],[d,e,f]);
f(a) //returns d
f(b) //returns e
f(c) //returns f
f(x) //any other value returns undefined
First I was trying to solve this problem like it's commonly done in functional programming. (Example in Racket)
(define makeFunction
(lambda (domain image)
(lambda (x)
(cond
[(= x (first domain)) (first image)
[else ((makeFunction (rest domain) (rest image) x)]
)
)
)
)
However something like this isn't possible in js, since the Function constructor doesn't create a closure (see here). Therefor my second attempt was to simply stringify the arrays and include them in the function definition (I also included input checking):
function makeFunction(domain, image){
if(new Set(domain).size==domain.length&&domain.length==image.length){
return new Function("x",`return ${JSON.stringify(image)}[${JSON.stringify(domain)}.indexOf(x)]`);
}else{
throw new Error("The lists don't allow the creation of a function, because they either don't have the same length or the domain isn't unique.");
}
}
This function works as long as primitive data types are the only ones the domain contains. But as soon as it includes objects, or even cyclic data, I've no idea how to make this function work...
//works as expected:
let f = makeFunction([1,2,3,4,"foo"],[4,3,2,1,"bar"]);
//always returns undefined:
let g = makeFunction([{foo:"bar"},{bar:"foo"}],[{bar:"foo"},{foo:"bar"}]);
//returns an error, since JSON.stringify doesn't work on cyclic data:
let cyclicData = {};
cyclicData.self = cyclicData;
let h = makeFunction([cyclicData],["foo"]);
Hopefully you can help me with this one :)
You noticed the pain you experience with new Function but you could've constructed a function using the function keyword like you did for makeFunction.
The following JavaScript function literal syntaxes below create lexicographic closures.[1] We'll be using the last syntax because it's closest to Racket
function makeFunction (param1, param2, ...) { statement1; statement2; ... }
const makeFunction = function (param1, param2, ...) { statement1; statement2; ... }
const makeFunction = (param1, param2, ...) => { statement1; statement2; ... }
const makeFunction = (param1, param2, ...) => expression
Your Racket program translates directly into JavaScript so long as we provide the first and rest functions. Note that isEmpty was added so that makeFunction ([], []) still works
const isEmpty = (xs = []) =>
xs.length === 0
const first = (xs = []) =>
xs [0]
const rest = (xs = []) =>
xs.slice (1)
const makeFunction = (domain = [], image = []) =>
x =>
isEmpty (domain)
? undefined
: x === first (domain)
? first (image)
: makeFunction (rest (domain), rest (image)) (x)
const f =
makeFunction ([ 'a', 'b', 'c' ], [ 'd', 'e', 'f' ])
console.log (f ('a')) // d
console.log (f ('b')) // e
console.log (f ('c')) // f
console.log (f ('x')) // undefined
Array destructuring assignment and default arguments allow us to see another way we could write our functions
const Empty =
Symbol ()
const isEmpty = ([ x = Empty, ...xs ]) =>
x === Empty
const first = ([ x = Empty, ...xs ]) =>
x
const rest = ([ x = Empty, ...xs ]) =>
xs
Or we can skip creating intermediate functions first and rest and use destructuring syntaxes directly in makeFunction
const Empty =
Symbol ()
const makeFunction = ([ key = Empty, ...keys ], [ value = Empty, ...values ]) =>
x =>
key === Empty
? undefined
: x === key
? value
: makeFunction (keys, values) (x)
const f =
makeFunction ([ 'a', 'b', 'c' ], [ 'd', 'e', 'f' ])
console.log (f ('a')) // d
console.log (f ('b')) // e
console.log (f ('c')) // f
console.log (f ('x')) // undefined
As #user3297291 points out, you should be looking at Map as it can look up a key in logarithmic time – compared to linear (slower) time used by makeFunction
[1] Functions using arrow (=>) syntax have also have a lexicographical this value
You can return a function that returns a function, this will create a closure for you.
Example:
makeFunction = (domain, image) => ((element) => (image[domain.findIndex(d => d === element)]))
f = makeFunction([1,2,3,4, 'foo'], [4,3,2,1, 'bar']);
f(1) // 4
f(2) // 3
...
Have you looked at using javascript's Map? Its get method is almost what you're trying to implement:
const makeFunction = (domain = [], image = []) => {
const m = new Map(pairs(domain, image));
return m.get.bind(m);
};
let a = { foo: "bar" };
let f = makeFunction([1,2,3,4,"foo"],[4,3,2,1,"bar"]);
let g = makeFunction([ a ]);
let h = makeFunction([ a ], [ 1 ]);
console.log(f(1));
console.log(g(a));
console.log(h(a));
// This still won't work though:
console.log(h({ foo: "bar" }));
function pairs([x, ...xs], [y, ...ys], ps = []) {
return x && y
? pairs(xs, ys, [...ps, [x, y]])
: ps;
};
Its keys however are still checked "by reference". If you want { foo: "bar" } == { foo: "bar" } you'll have to write some custom equality comparer...