函数式编程:IF 的替代方案 #Functional #JavaScript #Functors
解决方案 A:ifVal 辅助函数
解决方案 B:函子
结尾
示例一
示例二
例三
有几次我被问到“在函数式编程中你将如何做 X?”我非常喜欢这些类型的问题。
我会尽力回答每一个问题,但我认为有很多问题足够有趣,值得单独写一篇文章。
因此,在本文中,我想演示如何以更实用的方式重新创建这个命令功能。
此函数有一个if
没有 的语句else
。因此,虽然ternary
运算符可以工作,但并不理想。
// 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 })
}
}
这里我们只需要dispatch
在有的时候运行value
,否则我们什么也不做。
一种选择是使用短路运算符:
// 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
}
短路和三元以及两种方法都可以解决这个问题,但让我们再看几个。
解决方案 A:ifVal 辅助函数
为了更实用地实现这一点,我将创建一个辅助函数。我会先用老套的方式编写这个辅助函数,然后再逐步分解,以便每个人都能轻松理解。
// 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: voilà!
const ifVal = (x, f) => x == null ? null : f(x)
现在我们可以修改我们的someAction
函数来ifVal
代替经典的if
块。
// functional alternative
const someAction = value => dispatch =>
ifVal(getItem(value), item => dispatch({ type: 'ACTION', item }))
以下是快速简便的 Twitter 截图的比较:
/**
* 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以了解其他有用且类似的功能。
解决方案 B:函子
我们也可以使用Maybe 类型。Maybe 类型要么包含Just
一个值,要么Nothing
包含一个确切值的类型。
对于这个例子,我将使用Sanctuary库中的 Maybe Type 。
它看起来有点像这样:
/* 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 类型非常简单,我之所以想使用它是因为它可以很好地协同map
工作Maybe
,所以它被赋予了一个特殊的名字,即Functor。
有些库(例如ramda-fantasy)的语法比较流畅,但本文使用的是 Sanctuary。不过,了解这两个库是等价的还是不错的。
const double = x => x * 2
// ramda-fantasy
Maybe.toMaybe(333).map(double) //=> Just(666)
// Sanctuary
map(double, toMaybe(333)) //=> Just(666)
例子:
const double = x => x * 2
// map is ignored
map(double, toMaybe(null)) //=> Nothing
// no more null exceptions!
map(val => val.something(), toMaybe(null)) //=> Nothing
// map is mapped
map(double, toMaybe(333)) //=> Just(666)
所有doSomething
与 Maybe 一起看起来会像这样:
import { toMaybe, map } from 'sanctuary'
const someAction = value => dispatch => {
const item = getItem(value)
const maybeValue = toMaybe(item)
map(item => dispatch({ type: 'ACTION', item }))(maybeValue)
}
这里我们有三个函数,getItem
,toMaybe
和map
,它们可以组合成一个新的函数。我们可以将值传递给这个新函数,它会首先流入getItem
,然后toMaybe
最终流入我们的map
。
const someAction = value => dispatch => pipe([
getItem,
toMaybe,
map(value => dispatch({ type: 'ACTION', value })),
], value)
最后再发一条完美的推文:
import { toMaybe, map, pipe } from 'sanctuary'
// imperative example
const someAction = value => dispatch => {
const item = getItem(value)
if (item != null) {
dispatch({ type: 'ACTION', item })
}
}
// maybe functor example
const someAction = value => dispatch => pipe([
getItem,
toMaybe,
map(value => dispatch({ type: 'ACTION', value })),
], value)
someAction(666)(dispatch) // Action is dispatched!
someAction(null)(dispatch) // Nothing happens
结尾
希望你喜欢这段小小的函数式学习之旅。如果你喜欢,或者遇到问题,或者想看更多类似的文章,请在评论区告诉我。如果你真的想激励我,请在这里或Twitter关注我;如果你喜欢这篇文章,也请分享出去!
干杯!
鏂囩珷鏉ユ簮锛�https://dev.to/joelnet/function-programming-how-would-you-if-no-else-javascript-59ai