The issue is that you never multiply the two values: value and interest.
You should therefore pass another function into the composition, which will multiply the two previous results.
This means that this function will need to get 2 arguments, while the other two only take one. In general a function could need any number of arguments. So the composer should be able to pass enough arguments to each function. Furthermore, functions may also return more than one value -- in the form of an array. These values should be made available as arguments for the next function in the chain, while keeping any previous returned values available as well.
Another thing is that although you have implemented compose to execute the functions from right to left, the sequence of function you pass seem to suggest you expect them to execute from left to right, first getDeposit, and then getInterest, even though in your case it works both ways. Still, I would suggest to switch their positions.
So here is how you can make all that work:
(()=>{
// Generic compose function to handle anything
const compose = (...fns) => (...args) => {
return fns.reduceRight((acc,fn)=>{
// Call the function with all values we have gathered so far
let ret = fn.apply(null, acc);
// If function returns a non-array, turn it into an array with one value
if (!Array.isArray(ret)) ret = [ret];
// Queue the returned value(s) back into the accumulator, so they can
// serve as arguments for the next function call
acc.unshift(...ret);
return acc;
}, args)[0]; // only return the last inserted value
};
const getInterest = (value) => {
return value < 1000 ? 0.01
: value < 10000 ? 0.02
: 0.03;
};
const multiply = (a, b) => a * b;
const getDeposit = (value) => value;
// Be aware the the rightmost function is executed first:
const calculator = compose(multiply, getInterest, getDeposit);
console.log(calculator(1000)) // Returns 20, which is 0.02 * 1000
})();
Alternative: pass along an object
The above implementation is not a pure compose implementation, since it passes not only the previous function result on to the next, but all previous functions results. This is not disturbing, and opens doors for more complex functions, but if you wanted to stick more to the original compose idea, you have a problem to solve:
As you want to have a function in the chain that only returns the rate, the next function in the chain will then only get the rate -- nothing else. With just that one piece of information it is of course not possible to calculate the result, which also needs the value as input.
You could "solve" this, by letting getInterest return an object, that not only has the rate in it, but also the value that was passed to it. You could also implement this with an array.
Here it is with an object implementation:
(()=>{
// Straightforward implementation:
const compose = (...fns) => (...args) => {
return fns.reduceRight((acc,fn)=>{
return fn(acc);
}, args);
};
// Return an object with two properties: value & interest
const getInterest = (value) => ({
value,
interest: value < 1000 ? 0.01
: value < 10000 ? 0.02
: 0.03
});
// Expect an object as argument, with two properties:
const getInterestAmount = ({value, interest}) => value * interest;
const getDeposit = (value) => value;
// Be aware the the rightmost function is executed first:
const calculator = compose(getInterestAmount, getInterest, getDeposit);
console.log(calculator(1000)) // Returns 20, which is 0.02 * 1000
})();
With this approach you can pass along objects that have many more properties, and so anything becomes possible.
accshould be the second argument of the reducer.