开发时如何使用函数式编程写出简洁美观的业务代码?
日常开发中一定存在经常困扰你的问题。
例如,用户界面返回的字段不符合预期:
const data = await fetchData()
const foo = data.a.b.c.d
// Uncaught TypeError: Cannot read property 'c' of undefined
例如,代码中有未处理的边界条件,在某些条件下触发:
const list = [{ name: 'tom', age: 18 }]
const { name } = list.find(person => person.age === 20)
// Uncaught TypeError: Cannot destructure property `name` of 'undefined' or 'null'
为了避免这种尴尬的情况,我们通常这样做代码使用丑陋的“防御编程”:
const data = await fetchData()
let foo
if (data && data.a && data.a.b && data.a.b.c) {
foo = data.a.b.c.d
}
const list = [{ name: 'tom', age: 18 }]
const person = list.find(person => person.age === 20)
let name
if (person && person.name) {
name = person.name
}
我不知道当我看到这样的代码时你的感受如何,但我太丑了。
而“也许”只是一个好东西,可以帮助你逃离空虚和不确定的茫茫大海。
什么是 Maybe
Maybe 是函数式编程的一个概念。它是一种常用的函数,通常用于处理函数式编程中潜在的空值问题。
讲理论没什么意思,我们看看怎么用。当然,不同的 Maybe 实现可以有不同的接口。这里我们使用民间传说提供的 Maybe 对象:
const Maybe = require('folktale/maybe')
一个 Maybe 对象只能存在两种状态:Just(value) 和 Nothing
const Maybe = require('folktale/maybe')
// Just 可以直接 .get()
Maybe.Just(42).get()
//=> 42
// Nothing.get() 会报错
Maybe.Nothing().get()
//=> TypeError: Can't extract the value of a Nothing.
// 所以为了安全起见,一般使用 getOrElse() 来处理 Nothing
Maybe.Just(42).getOrElse(666)
//=> 42
Maybe.Nothing().getOrElse(666)
//=> 666
// 此外,.map() 还可以自动区分 Just 和 Nothing
Maybe.Just(42).map(x => x + 1)
//=> Maybe.Just(43)
Maybe.Nothing().map(x => x + 1)
//=> Maybe.Nothing()
Maybe 对象的内部是不确定的,例如“薛定谔的猫”,在叠加状态。
也许在打开这个“盒子”之前,里面的东西都是正义(价值)和Nothing。最终状态只有在盒子打开时才能确定。
const Maybe = require('folktale/maybe')
const foo = Maybe.fromNullable(Math.random() >= 0.5 ? 'bar' : undefined)
//=> 50%概率为 Maybe.Just('bar'),50%概率为 Maybe.Nothing()
const unsafe_result = foo.get()
// 有50%的概率会报错:
// TypeError: Can't extract the value of a Nothing.
// 为了安全起见,我们使用 getOrElse()
const result = foo.getOrElse('nothing!!!')
// 50%:'bar',50%:'nothing!!!'
如何在实践中使用它
也许非常适合处理代码中的不确定性,因为它将不确定性的处理推迟到最终值而不是在代码中间,例如:
const Maybe = require('folktale/maybe')
const list = [{ name: 'tom', age: 18 }]
// 丑陋的防御式代码
const person = list.find(person => person.name === 'stark')
let ageText = 'No Age'
if (person && person.age !== undefined) {
ageText = `User Age: ${person.age}`
}
// Maybe
const ageText = Maybe.fromNullable(
list.find(person => person.name === 'stark')
)
.map(person => `User Age: ${person.age}`)
.getOrElse('No Age')
我们还可以使用也许是为了实现一个简单的安全获取: 在 UI 调用中使用 Mahlo 的 safe get 非常酷:
const data = await fetchData()
const firstUserName = get(data, 'list', 0, 'name').getOrElse('no user found')
如果使用防御性编程,代码会变得像下面这样恶心:
const data = await fetchData()
let firstUserName = 'no user found'
if (data && data.list && data.list.length > 0 && data.list[0].name !== undefined) {
firstUserName = data.list[0].name
}
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。