函数式编程处理IF块
曾几何时,经常有人问我,“如果让你用函数式编程来实现X,该怎么做?”我比较喜欢这类的问题。我尽最大能力思考这类有趣的问题。本篇文章,我用一种更加函数式的方式来实现优雅处理IF函数块的问题。下面的代码有一个if块,但是没有else。
// A basic redux-thunk action that only dispatches when value exists
const someAction = value => dispatch => {
const item = getItem(value)
if (item != null) {
dispatch({ type: 'ACTION', item })
}
}
这里我们需要判断item有值的时候走dispatch方法,否则什么都不做。有一种思路是使用短路逻辑或者三元函数。
// short circuit
const someAction = value => dispatch => {
const item = getItem(value)
item && dispatch({ type: 'ACTION', item })
}
// ternary
const someAction = value => dispatch => {
const item = getItem(value)
item ? dispatch({ type: 'ACTION', item }) : null
}
短路逻辑和三元函数虽然都能解决这个问题,我们这里用另外一种思路。
Solution A: ifVal Helper Function
为了采用函数式的方法解决这个问题,我们先写个help类函数来实现,后面也更容易理解。
// 1: old school
function ifVal (x, f) {
if (x == null) {
return null
} else {
return f(x)
}
}
// 2: convert to arrow function
const ifVal = (x, f) => {
if (x == null) {
return null
} else {
return f(x)
}
}
// 3: convert if/else to a ternary operator
const ifVal = (x, f) => {
return x == null ? null : f(x)
}
// 4: 简化写法
const ifVal = (x, f) => x == null ? null : f(x)
现在我们可以采用ifVal函数替代旧的if函数。
// functional alternative
const someAction = value => dispatch =>
ifVal(getItem(value), item => dispatch({ type: 'ACTION', item }))
我们现在比较两种情况:
/**
* execute the function if the value is not null or undefined
* @param {Object} val - the value to test
* @param {Function} fn - the function to execute.
* @returns {Object} - null or the value of the executed function.
*/
const ifVal = (val, fn) => val == null ? null : fn(val)
// imperative example
const someAction = value => dispatch => {
const item = getItem(value)
if (item!= null) {
dispatch({ type: 'ACTION', item })
}
}
// functional example
const someAction = value => dispatch =>
ifVal(getItem(value), item => dispatch({ type: 'ACTION', item }))
扩展阅读,可以参考下Ramda的when,unless, 和 ifelse 等其他函数。
Solution B: Functors
我们也可以使用MayBe Type,如果有值,返回Just type,否者就是Nothing type。我们使用Sanctuary 类库。
/*Examples of Sanctuary's Maybe*/
toMaybe(null) //=> Nothing
toMaybe(undefined) //=> Nothing
toMaybe(0) //=> Just(0)
toMaybe(false) //=> Just(false)
toMaybe(123) //=> Just(123)
toMaybe({ name: 'joel' }) //=> Just({ name: 'joel' })
MayBe Type是最简单的例子,这里之所以选择Maybe,是因为它完全兼容map数据。