The Lodash flow() function works, as shown in this question, by giving it a bunch of functions in an array. The result of the previous function will be applied to the next one.
And this is exactly what is problematic for my use case, as it throws away any arguments provided. I need to pass them on to the next function in line, instead of just the return value *).
*) Yes, the return value should get returned modified and be passed on the next function
Using flow() is just what I came up with. Any other solution achieving the goal is ok as well.
What it does:
_.flow( [ firstFn, secondFn ] )
( first, second, third ) => firstFn( first, second, third ) => return first * first
( first ) => secondFn( first )
What it should do:
_.flow( [ firstFn, secondFn ] )
( first, second, third ) => firstFn( first, second, third ) => return first * first
( first, second, third ) => secondFn( resultFromFirstFn, second, third )
Here's some code to demo it:
const checkLog = ( val, attr, group ) => {
console.log( val, attr, group )
return val
}
// This basically is "easy to use"-API and what will be mostly used.
const Callbacks = {
demo: [ _.trim, _.toLower, checkLog ],
}
// _.cond() is just a switch
// [ <condition-to-exec-cb>, <cb-if-cond-true> ]
const cond = _.cond( _.map(
[
[ _.stubTrue, Callbacks.demo ],
],
// Monkey patching _.flow() to each array of cond/cb above
cb => _.set( cb, '1', _.flow( _.last( cb ) ) )
) )
const res = _.map( {
" city": " Sample City",
"street": "Sample Street",
}, cond )
console.log( res )
Note: In case you wonder about why I write it like this: There's a single line comment that points to which part will get altered and extended a lot in the future and this must, due to the target group working there, be simple. So it's just an array of stacked callbacks.
The problem with _.flow() is that it expects everything after the first argument to be a unary function so it can pass the result through all of them. This presents a challenge when you want to apply the same set of arguments to all function and only alter the first one. There are a few ways you could go about this.
Using Lodash
You could leverage _.partialRight to do a partial allocation of every function. partialRight will apply arguments from right to left depending on how many arguments the function takes.
const fn = (a, b, c, d) => console.log(a, b, c, d);
// behaves similar to (a, b) => console.log(a, b, "yak", "zebra")
const newFn = _.partialRight(fn, "yak", "zebra");
newFn("alpaca", "beaver");
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
However you can still pass arguments left to right and this would push the partially applied arguments to right:
const fn = (a, b, c, d) => console.log(a, b, c, d);
// behaves similar to (a, b) => console.log(a, b, "yak", "zebra")
const newFn = _.partialRight(fn, "yak", "zebra");
newFn("alpaca", "beaver", "cat");
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
Assuming all of the function will have at most take n amount of arguments we can:
Get an array of n arguments to be applied to all functions.
Get all functions.
Do partialRight on all functions using all but the first argument. Since we've applied n-1 arguments to each, now all of them can be called as if they are unary.
Use flow on the new functions from step 3.
Start the chain using the first argument.
This will then call all functions such that the first argument is the last result and the second and onwards arguments would be based on the initial ones thus always the same for all functions
function nAryFlow(argsArray, ...fns) { //1. and 2. - get arguments and functions
const start = argsArray[0];
const rest = argsArray.slice(1);
const convertedFns = fns.map(f => _.partialRight(f, ...rest)) //3. Turn all functions into unary ones
return _.flow( //4. `flow` through the functions
...convertedFns
)(start); //5. use the initial fist argument to kick things off
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const args = ["Alice", "Bob", "Carol"];
const result = nAryFlow(args, fn1, fn2, fn3);
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.19/lodash.min.js"></script>
Without using Lodash
All of the above can very easily be done without Lodash. Instead, we can use Array#reduce to go through all of the functions and apply the arguments. This time, we're directly applying them instead of pre-processing the functions but the overall operation and effect is the same:
The first function takes all the arguments.
Any further function takes the result of the last function and the second argument onwards from the start:
function nAryFlow(argsArray, ...fns) {
const start = argsArray[0];
const rest = argsArray.slice(1);
return fns.reduce((last, f) => f(last, ...rest), start);
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const args = ["Alice", "Bob", "Carol"];
const result = nAryFlow(args, fn1, fn2, fn3);
console.log(result)
Variation using more higher order functions
Just as a variation, this could be split up into multiple higher order functions which might produce a nicer syntax for some situations nAryFlow(f1, f2, f3, fN)(arg1, arg2, arg3, argN):
function nAryFlow(...fns) {
return function (...args) {
const start = args[0];
const rest = args.slice(1);
return fns.reduce((last, f) => f(last, ...rest), start);
}
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const chain = nAryFlow(fn1, fn2, fn3);
const result1 = chain("Alice", "Bob", "Carol");
const result2 = chain("Rachel Green", "Monica Geller", "Chandler Bing");
console.log(result1);
console.log(result2);
Using a Functor
For a slightly different take here, you can use an algebraic structure called Functor to ease the syntax. The essential thing about functors is that they have a .map() method that accepts a function. If you're reminded of Array#map then you're not wrong.
The basic idea is that the functor holds a value and allows it to be modified with functions via .map(). It can then dictate how the function is applied to its value. The result of .map is always the same type of functor as what was already mapped, so you can always continue mapping and be sure that the application would be the same every time. With arrays you always get a new array of the same length with every member transformed. Other functors can apply the function given to .map() different to what an array does but it would always be consistent.
So, background done, here is how this functor can look like:
class NAryFlow {
constructor(argsArray) {
this.value = argsArray[0];
this.rest = argsArray.slice(1);
}
static of(argsArray) {
return new NAryFlow(argsArray);
}
map(fn) {
return NAryFlow.of(
[ fn(this.value, ...this.rest), ...this.rest ]
);
}
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const result = NAryFlow.of(["Alice", "Bob", "Carol"])
.map(fn1)
.map(fn2)
.map(fn3)
.value;
console.log(result)
A similar idea as the two others above - we take the arguments, and apply them all to each function we give to .map(). Every next time we call .map() the first argument would be the last result.
And here is a slight variation using ES6 getters. I think it has a slightly better syntax but wanted to keep the previous implementation simpler.
class NAryFlow {
constructor(argsArray) {
this.args = argsArray;
}
static of(argsArray) {
return new NAryFlow(argsArray);
}
map(fn) {
return NAryFlow.of(
[ fn(...this.args), ...this.rest ]
);
}
get value() {
return this.args[0];
}
get rest() {
return this.args.slice(1);
}
}
const fn1 = (who1, who2, who3) => `${who1}, ${who2}, and ${who3} are best friends.`;
const fn2 = (friends, who2, who3) => `${friends} Especially ${who2} and ${who3}.`;
const fn3 = (story, who2) => `${story} They all met at a party thrown by ${who2}`;
const result = NAryFlow.of(["Alice", "Bob", "Carol"])
.map(fn1)
.map(fn2)
.map(fn3)
.value;
console.log(result)
flow doesn't throw any arguments, it's just how javascript functions work, they don't respect the a particular signature, you can call a function that only takes 1 parameter with 100 parameters and that won't throw an error.
Also flow is defined such that it passes the initial parameters all to the first function regardless of whether it's going to use them all or not.
You can create your own flow function(since it is a simple one) that only passes the exact amount of parameters a function needs and keep the rest for the rest of functions. To know how many parameters a function takes, you can check its length property like so:
function flow(funcs) {
const length = funcs.length;
for(let func of funcs) {
if (typeof func !== 'function') {
throw new TypeError('Expected a function');
}
}
return function(...args) {
let result = args[0];
for(let func of funcs) {
result = func.apply(this, args.slice(0, func.length)); // call this function with only as many args as it needs
args = [result, ...args.slice(func.length)]; // args becomes the result + the rest of args after the previous call
}
return result;
}
}
Note: This won't work if one of the functions uses the rest parameters, in that case length will return 0. You can't know how many parameters that functions takes. If you wan to call that function with all the available parameters, then just change the for loop above to:
for(let func of funcs) {
result = func.apply(this, args.slice(0, func.length || +Infinity)); // if 'func.length' is 0, '+Infinity' is used instead which will use all the available args
args = [result, ...args.slice(func.length || +Infinity)]; // same goes here, '.slice(+Infinity)' will result in an empty array. 'result' will always be present which is the expected behavior
}
Demo:
function flow(funcs) {
const length = funcs.length;
for(let func of funcs) {
if (typeof func !== 'function') {
throw new TypeError('Expected a function');
}
}
return function(...args) {
let result = args[0];
for(let func of funcs) {
result = func.apply(this, args.slice(0, func.length)); // call this function with only as many args as it needs
args = [result, ...args.slice(func.length)]; // args becomes the result + the rest of args after the previous call
}
return result;
}
}
function square(n) {
return n * n;
}
function add(a, b, c) {
return a + b + c;
}
console.log(flow([square, add])(2, 3, 4));
Say one has a function that, among other tasks, makes a few api calls using axios. Is there a way, when testing this function, to mock all the axios api calls and specify return values from the calls depending on the input. For example, say the function you want to test is this:
function someFunction (a, b, c) {
const apiReturnA = axiosApiCall(a)
const returnB = b + 1
const apiReturnC = axiosApiCall(c)
return [apiReturnA, returnB, apiReturnC]
}
I'd like to test someFunction and specify that, every time axiosApiCall gets called, don't execute the function, simply return a value based on the input to this function. How can one do this?
You could inject axiosApiCall via a closure:
function apiWrapper(axiosApiCall) {
return function someFunction (a, b, c) {
const apiReturnA = axiosApiCall(a)
const returnB = b + 1
const apiReturnC = axiosApiCall(c)
return [apiReturnA, returnB, apiReturnC]
}
}
const testApiCall = function(input) {
return input * 2;
}
let someFunction = apiWrapper(testApiCall);
console.log(someFunction(1,2,3));
And then in your real code you just pass in the real axiosApiCall
Having two functions one wrapping the other, how to pass a parameter down correctly? At the moment the console logging does not work.
const a = (v) => (v) => console.log(v);
a(2);
Passing a parameter in High order fn is easy as the value get set to closure
const a = v => () => {console.log(v)};
a(2)()
No matter how deep you go, parameter passed to fn get set to closure space when a fn having a parameter v either returns a fn or executes a fn which uses that parameter v
const a = v => () => () => () => {console.log(v)}
a(2)()()()
If you really want to work on two functions
const a = (v) => (a = v) => console.log(a);
a(2)()
What you did over there to define a lambda function within a function.
If you want it to work with a(2) you need to excecture the inner function (meaning to add () at the end), like this:
const a = (v) => (() => console.log(v))();
a(2);
This (() => console.log(v)) is a function, when you add () at the end it's actually activating that function, and that's why what you did, didn't work.
You have two v variables, and the inner one is shadowing the outer one since they have the same name. Unless you rename the inner v, there's no way to access the outer v from inside that function.
Let's say I have a function that takes a function as an argument and returns another function that takes a value that it passes to the function passed as an argument:
const curry = f => x => f(x);
Is it possible to type this function using Flow Type in such a way that the return value of curry is determined by the function f passed in if that function has been typed?
I tried this:
const numToStr = (x: number): string => x.toString();
const curry = <F: Function>(f: F): (mixed => *) => (x: mixed): * => f(x);
const curried = curry(numToStr);
link
The result of passing the first argument is that the resulting function curried has a signature like this:
(((x: mixed) => any) | ((x: mixed) => empty))
This makes sense, either the result is empty or it's anything. What I'd hoped is that because numToStr is typed, that it'd be possible for Flow to understand that curried is really number => string.
Is there a way to do this that I've missed? If not, can anyone explain why.
Currying usually applies to the process of taking a function of two arguments and transforming it into a function of one argument that returns another function of one argument which returns the result.
In Flow it would look like this:
const curry = <A, B, C>(f: (A, B) => C): (A => (B => C)) => (x) => (y) => f(x, y);
const prefixNum = (prefix: string, x: number): string => prefix + x.toString();
const prefixed = curry(prefixNum);
const withHello = prefixed("hello ");
const result = withHello(3); // "hello 3"
Your version is a bit different. You are transforming a function of one argument into a function of zero arguments. This is more like lazy evaluation than currying: supply all of the arguments but only return a zero-argument function (sometimes called a "thunk"), which actually performs the computation when you call it. The implementation could look like this:
const makeLazy = <A, C>(f: A => C): (A => (() => C)) => (x) => () => f(x);
It's essentially the definition above, but with one of the types replaced with (). This should work how you want it to:
const numToStr = (x: number): string => x.toString();
const lazyNumToStr = makeLazy(numToStr);
const thunk = lazyNumToStr(3);
thunk(); // "3"
I want to better understand es6 arrow functions.
Given the following example:
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
// snip actual enhancer logic
return {
...store,
dispatch
}
}
}
Describing the above in words:
Our exported function (applyMiddleware) takes an array parameter with spread.
Then applyMiddleware returns a nameless function with a createStore parameter, which returns another nameless function this time with three parameters.
So without the arrows it would look like this:
export default function applyMiddleware(...middlewares) {
return function(createStore){
return function(reducer,preloadedState,enhancer){
//some logic
return{...store,dispatch}
}
}
}
My questions:
Am I correct?
What is the common use case/code paradigm we see here?
The answer to your first question is more or less (see my comment). The answer to your second question is that the pattern you are seeing is a combination of using a closure and currying. The original parameters to the exported function get gathered into an array called 'middlewares' that the returned functions close over (i.e. have access to). The function then can be called again with yet another parameter 'createStore' then another function is returned that can accept even more parameters. This allows one to partially apply the parameters. For a more trivial (and perhaps more easily comprehended) example, lets take a function called 'add' that adds two numbers:
let add = (x, y) => x + y;
Not very interesting. But lets break it up so it can take the first number and return a function that takes the second:
let add = x => y => x + y;
let add3 = add(3);
let seven = add3(4); // 7
This may not seem like a big win for our add function, but you started with a much more reasonable real-world example. Additionally, rather than currying manually it is possible (and desirable) to use a curry function that does it for you, many popular libraries (lodash, underscore, ramda) implement curry for you. An example using Ramda:
let add = R.curry((x, y) => x + y);
let add3 = add(3);
let five = add3(2);
let also5 = add(3, 2);
let still5 = add(3)(2); // all are equivalent.
This answer is for those who still have some doubts in double arrow functions. Lets dig deep into it.
const doubleArrowFunc = param1 => param2 => {
console.log('param1', param1);
console.log('param2', param2);
}
If you call this function
const executeFunc = doubleArrowFunc('Hello');
If you print executeFunc in console you'll get an output like this
ƒ (param2) {
console.log('param1', param1);
console.log('param2', param2);
}
Now this is like a half executed code. If you wanna execute it fully, you've to do it like this
executeFunc('World');
//And the output will be
param1 Hello
param2 World
If you want even more understanding. I can execute the same without arrow function
function doubleArrowFunc(param1) {
return function innerFunction(param2) {
console.log('param1', param1);
console.log('param2', param2);
}
}