I was reading this article at hackernoon.com that tries to explain a particular approach to turn javascript Switch/Case into a functional version of it self.
The article reaches a point where they set a function called switchcase
, defined as:
const switchcase = cases => defaultCase => key =>
cases.hasOwnProperty(key) ? cases[key] : defaultCase
They also say that this function has a problem, as "the entire object literal is evaluated before being passed to the switchcase function". And so they decide to convert the values in the object literal to functions. To do so they utilize this syntax:
const switchcaseF = cases => defaultCase => key =>
switchcase(cases)(defaultCase)(key)()
My question is: How does this last syntax work? Can anyone break it down for me?
By using functions, you need to take a function for defaultCase
as well.
const switchcase = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase const switchcaseF = cases => defaultCase => key => switchcase(cases)(() => defaultCase)(key)() // ^^^^^^^^^^^^^^^^^ ^^ console.log(switchcaseF({ foo: () => 'bar' })('nothing')('foo')); console.log(switchcaseF({ foo: () => 'bar' })('nothing')('baz'));
It would be cleared if rewrite this with simple function.
First, rewrite switchcase
. This function is a result of currying next function with three arguments
function switchcase(cases, defaultCase, key){
if(cases.hasOwnProperty(key)){
return cases[key];
}
return defaultCase;
}
So, if rewrite switchcaseF
we get next:
function switchcaseF(cases, defaultCase, key){
var selectedFunc = switchcase(cases, defaultCase, key);
return selectedFunc();
}
How does this last syntax work? Can anyone break it down for me?
Consider the definition of switchcase
and switchcaseF
.
const switchcase = cases => defaultCase => key =>
cases.hasOwnProperty(key) ? cases[key] : defaultCase
const switchcaseF = cases => defaultCase => key =>
switchcase(cases)(defaultCase)(key)()
If we inline the application of switchcase
within switchaseF
we get the following.
const switchcaseF = cases => defaultCase => key =>
(cases.hasOwnProperty(key) ? cases[key] : defaultCase)()
//|____________________________________________________|
// |
// switchcase(cases)(defaultCase)(key)
Furthermore, we can move the function application inside the conditional expression.
const switchcaseF = cases => defaultCase => key =>
cases.hasOwnProperty(key) ? cases[key]() : defaultCase()
Now, consider the example from the article that you linked to.
const counter = (state = 0, action) =>
switchcaseF({
'INCREMENT': () => state + 1,
'DECREMENT': () => state - 1
})(() => state)(action.type)
If we inline the application of switchcaseF
within counter
we get the following.
const counter = (state = 0, action) => {
const cases = {
'INCREMENT': () => state + 1,
'DECREMENT': () => state - 1
}
const defaultCase = () => state
const key = action.type
return cases.hasOwnProperty(key) ? cases[key]() : defaultCase()
}
Hence, if action.type
is 'INCREMENT'
then the result is state + 1
. If action.type
is 'DECREMENT'
then the result is state - 1
. Otherwise, the result is state
.
The reason we write expressions like () => state + 1
instead of simply state + 1
is for lazy evaluation . We only evaluate the body of () => state + 1
when the function is called. This prevents incorrect behaviour like in the following example.
const switchcase = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase const never = () => { while (true); } const example = key => switchcase({ never: never() })('it works')(key) console.log(example('it should work')) // expected 'it works' but never returns
Using switchcaseF
solves this problem.
const switchcaseF = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key](): defaultCase() const never = () => { while (true); } const example = key => switchcaseF({ never: () => never() })(() => 'it works')(key) console.log(example('it should work')) // 'it works' as expected
However, note that you can use getters to make it work with switchcase
too.
const switchcase = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase const never = () => { while (true); } const example = key => switchcase({ get never() { return never(); } })('it works')(key) console.log(example('it should work')) // 'it works' as expected
We can also make defaultCase
lazy.
const switchcase2 = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase.value const never = () => { while (true); } const example = key => switchcase2({ get never() { return never(); } })({ get value() { console.log('yes'); return 'it works'; } })(key) console.log(example('it should work')) // 'yes' 'it works' as expected
If you don't want it to be lazy then you can wrap it in strict
as follows.
const switchcase2 = cases => defaultCase => key => cases.hasOwnProperty(key)? cases[key]: defaultCase.value const never = () => { while (true); } const strict = value => ({ value }) const example = key => switchcase2({ get never() { return never(); } })(strict('it works'))(key) console.log(example('it should work')) // 'it works' as expected
Hope that elucidates your doubts.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.