添加链接 注册    登录
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
爱喝酒的圣诞树  ·  TIPS:.gitignore忽略多层文件夹 ...·  1 年前    · 
儒雅的保温杯  ·  显存不够,框架来凑:两行代码显存翻倍,208 ...·  1 年前    · 
逃课的消炎药  ·  用Newtonsoft将json串转为对象的 ...·  2 年前    · 
安静的麻辣香锅  ·  如何用cgroup ...·  2 年前    · 
酷酷的仙人球  ·  js获取<input ...·  2 年前    · 
link之家  ›  函数式编程中的 algebraic effects 是什么? - Malcolm Yu 的回答 -
calc getinfo 逻辑函数 函数式编程
https://www.zhihu.com/question/300095154/answer/1744221759
文武双全的板栗
2 年前
函数式编程中的 algebraic effects 是什么?
Malcolm Yu
Malcolm Yu
https://malcolmyu.github.io/

algebraic effects 难以理解,主因还是翻译的锅:大部分文章将其译作『 代数效应 』,实际上它表达的含义大致是『可以当做参数传递的副作用』。

从实用的角度上举例,假如我们有这样一段代码,其主要目的是进行一大段精妙的运算:

async function biz(id) {
  const infoId = /* do some calc */ id; // 这里可以理解为是一大段计算逻辑
  const info = await getInfo(infoId);   // 副作用,与 server 通信
  const dataId = /* do some calc */ info.dataId; // 这里可以理解为是一大段计算逻辑
  const data = getData(dataId);         // 副作用,非幂等操作
  return /* do some calc */ data.finalCalcData;  // 这里可以理解为是一大段计算逻辑

尽管运算逻辑很优美,但美中不足的是有两段副作用,导致它不能成为一个干净的纯函数被单元测试。

而且这里会导致严重的逻辑耦合:『做什么』与『怎么做』没有拆的很干净:

  1. 你的一大段计算逻辑是在处理做什么;
  2. 两个副作用更关心怎么做:比如线上是接口调用,单测里是 mock 数据直接怼;
  3. 但是由于这两块副作用代码,导致整个糅杂的逻辑都无法复用。

看到这里你可能会一拍大腿:函数在 JS 里不是一等公民嘛,我直接把两个副作用传进来不就行了?

async function biz(id, getInfo, getData) {
  const infoId = /* do some calc */ id; // 这里可以理解为是一大段计算逻辑
  const info = await getInfo(infoId);   // 副作用,与 server 通信
  const dataId = /* do some calc */ info.dataId; // 这里可以理解为是一大段计算逻辑
  const data = getData(dataId);         // 副作用,非幂等操作
  return /* do some calc */ data.finalCalcData;  // 这里可以理解为是一大段计算逻辑

是的,这样确实可以复用,但还有一个叫函数染色的 [1] 问题没有解决:明明是一大段干净的同步运算逻辑,因为 getInfo 是异步的,导致整个函数都得加个 async。而且很有可能在我单元测试里,这个 getInfo 是直接同步取内存数据,还得因此弄个 Promise……

这时候如果 JS 里有这样一种语法就好了:

function biz(id) {
  const infoId = /* do some calc */ id; // 这里可以理解为是一大段计算逻辑
  const info = perform { type: 'getInfo', payload: infoId };
  const dataId = /* do some calc */ info.dataId; // 这里可以理解为是一大段计算逻辑
  const data = perform { type: 'getData', payload: dataId };
  return /* do some calc */ data.finalCalcData;  // 这里可以理解为是一大段计算逻辑
// 正常业务逻辑
async function runBiz() {
  try {
    biz();
  } handle(effect) {
    if (effect.type === 'getInfo') {
      resume await getInfo(effect.payload);
    } else if (effect.type === 'getData') {
      resume getData(effect.payload)
// 单元测试逻辑
function testBiz() {
  try {
    biz();
  } handle(effect) {
    if (effect.type === 'getInfo') {
      resume testInfo;
 
推荐文章
爱喝酒的圣诞树  ·  TIPS:.gitignore忽略多层文件夹用**_gitignore子目录-CSDN博客
1 年前
儒雅的保温杯  ·  显存不够,框架来凑:两行代码显存翻倍,2080Ti也能当V100来用_模型
1 年前
逃课的消炎药  ·  用Newtonsoft将json串转为对象的方法(详解)_C#教程_脚本之家
2 年前
安静的麻辣香锅  ·  如何用cgroup v2从容器内获取docker容器ID?
2 年前
酷酷的仙人球  ·  js获取<input type='file'>的文件名和后缀名 - _DC - 博客园
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
link之家 - 链接快照平台
© 2024 ~ 沪ICP备11025650号