本文要点
William Heslam 是 Ecotricity 的高级 JavaScript 开发人员。最近,他在 Lambda Days 2020 上做了一场演讲,内容是关于利用可扩展效果进行副作用建模以及由此带来的测试方面的好处。
Heslam 的演讲围绕着一个具体的例子展开,那是一个处理待办列表的应用程序后端(即Todo MVC基准应用程序),下面是相应的 API:
// 监听请求
const update = api.put('/update',
// 写入数据库
(request, response) =>>'todos',
request.items.filter(hasTitle)
// 响应客户端
.then(result => response({
added: request.items.filter(hasTitle),
notAdded: request.items.filter(hasNoTitle)
.map(item => ({ reason: 'NEEDS_TITLE', item }))
复制代码
在对如何应用标准测试技术(如模仿和依赖注入)进行 API 测试做了一个快速的回顾之后,Heslam 利用freer monad引入了可扩展效果来表达这个 API:
// 监听请求
const update = api.put('/update')
// 写入数据库
.chain(request =>>'todos',
request.items.filter(hasTitle)
// 响应客户端
.chain(result => api.response({
added: request.items.filter(hasTitle),
notAdded: request.items.filter(hasNoTitle)
.map(item => ({ reason: 'NEEDS_TITLE', item }))
复制代码
api.put 返回一个 etchRune 函数生成的数据结构,该函数来自 Heslam 的可扩展效果库:
const api = {
put: url => etchRune({ type: 'API_PUT', url }),
response: payload => etchRune({ type: 'API_RESPONSE', payload }),
复制代码
update 变量是一个数据结构,它可以对一个程序进行有效地编码(就像一个抽象语法树(AST)),通过提供一个解释器,我们就可以对这个程序做各种处理。Heslam 提供了下面这个解释器的例子,它将逐步记录 update 程序的执行情况:
const result = update.interpret(effect => {
if(effect.type === 'API_PUT') {
return State.modify(log => [...log, effect])
.map(() => ({ items: [
{ title: 'Hungry', desc: 'Buy hummus!' },
{ desc: "Finish lambda days talk..." }
} else if(effect.type === 'DATABASE_BATCH_WRITE') {
return State.modify(log => [...log, effect])
.map(() => 'success!')
} else if(effect.type === 'API_RESPONSE') {
return State.modify(log => [...log, effect])
.map(() => 42)
})(crocksStateAPI)
复制代码
Heslam 最后提供了一个解释器,它将随机输入生成与执行跟踪生成相结合,为基于属性的测试提供支持。
InfoQ 采访了 Heslam,以了解可扩展效果在 JavaScript 开发中的适用范围和好处。
InfoQ:请给我们介绍下你的经历,以及如何对函数式编程产生兴趣的?
InfoQ:在演讲中,你展示了一个应用程序的 API,说其实现效果可以利用 可扩展效果 实现,并提到了你正在开发的可扩展效果库,其灵感来自 freer monad 。通俗地讲,freer monad 是什么?那些可扩展效果又是什么?
InfoQ:在过去的几年中,可扩展效果似乎是许多 Haskell 论坛上的一个热门话题,因为 Haskell 开发人员经常比较构造程序的正确方法。JavaScript 开发人员并不一定会对可扩展效果感兴趣——构造程序的正确方法通常会涉及到对前端框架的讨论。如何向 JavaScript 开发人员(或其他不使用纯函数式语言的开发人员)推销可扩展效果?
InfoQ:与注入效果处理程序相比,可扩展效果的优势是什么(例如, ReaderT设计模式 做的事情就类似于依赖注入)?
InfoQ:反过来说,可扩展效果有什么缺点或限制吗?Sandy Maguire 有 一篇文章 介绍了纯函数式语言上下文中的一些限制。这些限制是否适用于 JavaScript 上下文?
InfoQ:有许多可扩展效果库是用 JavaScript 编写的吗?能列举一些吗?Runic 库设计目标是什么?想要包含什么特性?每个特性在什么时间点完成?
InfoQ:在演讲中,你使用 Runic 库和可扩展效果测试了一个示例 API。这样做的原因是,一旦你有一个 free® monad 程序,你就可以附加选择的任何解释器来进行程序计算。解释器可以在程序中执行这些效果。出于测试目的,建议使用另一种解释器,它可以生成程序的随机输入,进行程序计算,跟踪程序计算过程,包括计算期间发生的错误。我理解的对吗?在解释器中处理或模拟错误有什么好处?
InfoQ:基于属性的测试如何与解释器机制一起工作?
InfoQ:除了演讲之外,是否有机会在真实的程序中使用可扩展效果?如果有的话,能详细说明下使用的背景以及在这种背景下获得的好处吗?
InfoQ:你认为如何才能增强可扩展效果对 JavaScript 开发人员的吸引力,无论是在前端还是后端?
受访者介绍 :
William Heslam 全栈 Web 开发人员和国际演讲者。他感兴趣的是如何通过借鉴函数式编程领域令人兴奋的思想来简化和改进软件开发。他喜欢就各种主题发表演讲,从复杂的测试技术到并行生物模拟。Heslam 的爱好包括寻找 20 世纪 80 年代被遗忘的范例和 3D 图形编程。他还喜欢伯爵茶、花椒和德国泡菜,但不是同时吃。
原文链接:
Extensible Effects in JavaScript for Fun and Profit - Q&A with William Heslam