Reputation: 9
I have tried to rewrite the compose function to print to the console with different arguments but I am still unable to figure it out. The first time the compose function is called arg
is undefined. So the reduce function would use the first element of the array (console.clear()) as the initial value and that would be used as the argument for the next function, getCurrentTime.
When compose is called with convertToCivilianTime it uses the returned value from serializeClockTime as the argument for the first returned function in compose.
So how is compose called the first time with an undefined arg
argument without causing an error?
const compose = (...fns) =>
(arg) =>
fns.reduce( (composed, f) => f(composed), arg)
const oneSecond = () => 1000
const getCurrentTime = () => new Date()
const clear = () => console.clear()
const log = message => console.log(message)
const serializeClockTime = date => ({
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds()
})
const civilianHours = clockTime => ({
...clockTime,
hours: ( clockTime.hours > 12 ) ? clockTime.hours - 12 : clockTime.hours
})
const appendAMPM = clockTime => ({
...clockTime,
ampm: ( clockTime.hours >= 12 ) ? "PM" : "AM"
})
const display = target => time => target(time)
const formatClock = format => time => format.replace('hh', time.hours).replace('mm', time.minutes).replace('ss', time.seconds).replace('tt', time.ampm)
const prependZero = key => clockTime => ({
...clockTime,
[key] : ( clockTime[key] < 10 ) ? "0" + clockTime[key] : clockTime[key]
})
const convertToCivilianTime = clockTime => compose(appendAMPM, civilianHours)(clockTime)
const doubleDigits = civilianTime => compose(
prependZero('hours'),
prependZero('minutes'),
prependZero('seconds')
)(civilianTime)
const startTicking = () => setInterval(compose(
clear,
getCurrentTime,
serializeClockTime,
convertToCivilianTime,
doubleDigits,
formatClock('hh:mm:ss tt'),
display(log)
),
oneSecond()
)
startTicking()
Upvotes: 0
Views: 109
Reputation: 29086
Passing undefined
as the initial argument to reduce
does not mean "use the default initial argument" (first element of the array) but just "use undefined
as the initial argument":
const arr = [1, 2, 3];
const traceSum = (a, b) => {
console.log(a, b)
return a + b
};
console.log("reduce without initial argument:", arr.reduce(traceSum));
console.log("reduce with initial argument:", arr.reduce(traceSum, 0));
console.log("reduce with initial argument undefined:", arr.reduce(traceSum, undefined));
So, it's safe to call a function created by compose
, even if you don't supply an argument, since arg
will simply be an explicity undefined
and thus each function gets executed but the initial value that goes through them is undefined
:
const compose = (...fns) =>
(arg) =>
fns.reduce( (composed, f) => f(composed), arg)
const trace = message => value => {
console.log(message, value);
return value;
}
const test = compose(trace("one"), trace("two"));
console.log(test("hello"));
console.log(test());
You just have to make sure that your composed functions can work without an initial value:
const compose = (...fns) =>
(arg) =>
fns.reduce( (composed, f) => f(composed), arg)
const excited = str => str + "!";
const loud = str => str.toUpperCase();
const greet = () => "hey";
const shout = compose(loud, excited);
const shoutGreeting = compose(greet, shout);
console.log(shout("stop")); //works with initial value
console.log(shoutGreeting()); //works without initial value
console.log(shoutGreeting("hammer time")); //ignores initial value
console.log(shout()); //error - initial value expected
Upvotes: 1
Reputation: 1968
When no argument is passed as the initialValue of the reduce function, the first time, it will call the callback with the first two elements of the array.
For example:
[1, 2, 3, 4, 5].reduce((a, b) => {
console.log(a, ',', b, ',', a + b)
return a + b;
}
It will result in the following logs:
1 , 2 , 3
3 , 3 , 6
6 , 4 , 10
10 , 5 , 15
Upvotes: 0