IO 与代数效应
IO 怎样把副作用变成纯描述?
IO: 封装副作用的纯数据结构;- 延迟: 用零参数 Thunk 保存动作,创建和组合时不执行;
- 触发: 只有显式调用
run,副作用才发生;
const IO = (run) => ({
run,
map: (fn) => IO(() => fn(run())),
chain: (fn) => IO(() => fn(run()).run()),
});
const readTimestamp = IO(() => Date.now());
const formatted = readTimestamp.map((ts) => new Date(ts).toISOString());
formatted.run();
代数效应怎样分离请求与处理?
- 程序: 只声明“读取配置”“记录日志”等效应,不绑定运行环境;
- 处理器: 截获效应、提供结果,并决定恢复或终止计算;
- 能力: 可统一表达异常、异步和生成器式控制流,减少业务对解释器的耦合;
const perform = (effect) => ({ effect: true, value: effect });
function* program(userId) {
const config = yield perform({ type: "askConfig" });
yield perform({ type: "log", message: `${config.apiUrl}/${userId}` });
return { id: userId };
}
简化处理器如何恢复生成器?
const handle = (iterator, handlers) => {
const step = (input) => {
const next = iterator.next(input);
if (next.done) return next.value;
const request = next.value.value;
return handlers[request.type](request, step);
};
return step();
};
handle(program(42), {
askConfig: (_effect, resume) => resume({ apiUrl: "https://api.test" }),
log: (effect, resume) => {
console.log(effect.message);
return resume();
},
});
IO 与代数效应的边界有什么不同?
| 机制 | 分离方式 |
|---|---|
IO | 把动作封装后延迟到 run |
| 代数效应 | 把效应请求交给外层处理器解释 |
- 共同目标: 让含副作用的程序仍能在纯数据与纯组合层面被描述;