[Function Programming] Function modelling -- 9. Monad Transformers
Path: Compose Functors -> Monad Transformers -> Free Monad
Let's first see how much it sucks when dealing with nested Monads (without natural transformation).
const { TaskT, Task, Either } = require("../types"); const _ = require("lodash"); const users = [ { id: 1, name: "Brian" }, { id: 2, name: "Marc" }, { id: 3, name: "Odette" }, ]; const following = [ { user_id: 1, follow_id: 3 }, { user_id: 1, follow_id: 2 }, { user_id: 2, follow_id: 1 }, ]; const find = (table, query) => Task.of(Either.fromNullable(_.find(table, query))); const app = () => find(users, { id: 1 }) // Task(Either(User)) .chain((eitherUser) => eitherUser.fold(Task.rejected, (user) => find(following, { follow_id: user.id }) ) ) .chain((eitherUser) => eitherUser.fold(Task.rejected, (foUser) => find(users, { id: foUser.user_id }) ) ) .fork(console.error, (eu) => eu.fold(console.error, console.log)); app(); // { id: 2, name: 'Marc' }
As you can see, the code is trying to find your follower's follower.
Each time we need to .chain() + .fold()...
Then .fork() + .fold()...
Which is confusing.
To solve the problem, we can include Monad transform:
const TaskEither = TaskT(Either);
Redefine 'find' function:
const find = (table, query) =>
TaskEither.lift(Either.fromNullable(_.find(table, query)));
Difference between .of() vs .lift():
// TaskEither.of(Either) --> Task(Either(Either)) // TaskEither.lift(Either) --> Task(Either)
Then we can simpify our app:
const app = () => find(users, { id: 1 }) // Task(Either(User)) .chain((user) => find(following, { follow_id: user.id })) .chain((foUser) => find(users, { id: foUser.user_id })) .fork(console.error, (eu) => eu.fold(console.log, console.log)); app();
--
Full ocde:
const { TaskT, Task, Either } = require("../types"); const _ = require("lodash"); const TaskEither = TaskT(Either); const users = [ { id: 1, name: "Brian" }, { id: 2, name: "Marc" }, { id: 3, name: "Odette" }, ]; const following = [ { user_id: 1, follow_id: 3 }, { user_id: 1, follow_id: 2 }, { user_id: 2, follow_id: 1 }, ]; const find = (table, query) => TaskEither.lift(Either.fromNullable(_.find(table, query))); const app = () => find(users, { id: 1 }) // Task(Either(User)) .chain((user) => find(following, { follow_id: user.id })) .chain((foUser) => find(users, { id: foUser.user_id })) .fork(console.error, (eu) => eu.fold(console.log, console.log)); app();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2017-06-29 [WASM] Read WebAssembly Memory from JavaScript
2017-06-29 [WASM] Call a JavaScript Function from WebAssembly
2016-06-29 [Webpack 2] Ensure all source files are included in test coverage reports with Webpack
2016-06-29 [Webpack 2] Add Code Coverage to tests in a Webpack project