[Compose] 21. Apply Natural Transformations in everyday work
We see three varied examples of where natural transformations come in handy.
Let's mark the law here for Natural Transformations:
nt(F).map(f) === nf(F.map(f))
Then let's see why we might need to apply Natural Transformations:
1. Natural Transformations can improve the code proformance
For example, we have an array of numbers, we want to get larger number, apply a doulbe function, then only get first element out of the array.
const first = xs => fromNullable(xs[0]); const largeNumbers = xs => xs.filter(x => x > 100); const doulbe = x => x * 2; // We first run though the whole array to doulbe the number // Then apply first to either transform -> Proformance cost // Since nt(F).map(f) === nt(F.map(f)) const transform1 = xs => first(largeNumbers(xs).map(doulbe)); const app = xs => transform1(xs); console.log(app([2,400,5,100])); // Either { value: 800 }
Imporved one should be get only first of larger number, then apply transform:
const first = xs => fromNullable(xs[0]); const largeNumbers = xs => xs.filter(x => x > 100); const doulbe = x => x * 2; // Now we get first of array and transform to either, then apply our doulbe function const transform2 = xs => first(largeNumbers(xs)).map(doulbe); const app = xs => transform2(xs); console.log(app([2,400,5,100])); // Either { value: 800 }
2. Solve different nested Functors:
const fake = id => ({id, name: 'user1', best_friend_id: id + 1}); // Db :: find :: id -> Task(Either) const Db = ({ find: id => new Task((rej, res) => { res(id > 2 ? Right(fake(id)): Left('not found')) }) });
For example, the code above, we have a Db.find function, which return Task(Either(User)). It would be much easier for us to work with the same Functor type, instead of multi functors. we can think about change
// Either -> Task
const eitherToTask = e => e.fold(Task.rejected, Task.of);
With this tool, let's see how it solve us problem:
Db.find(4) // Task(Right(User))
Now we chain 'eitherToTask':
Db.find(4) // Task(Right(User)) .chain(eitherToTask) // // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)
So after run though 'eitherToTask' we change 'Right to Task' we got 'Task(Task(User)), then the outmost 'chain' will remove 'Task(User)'
Now if we want to find User's best friend id:
Db.find(4) // Task(Right(User)) .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User) .chain(user => Db.find(user.best_friend_id)) // Task(User) --Db.find--> Task(Task(Right(User))) --chain--> Task(Right(User))
We know that 'Db.find(user.best_friend_id)' returns 'Task(Right(User))', inner most function call return 'Task(Task(Right(User)))', after outter most 'chain', we got 'Task(Right(User))'.
Now we can apply the trick again:
Db.find(4) // Task(Right(User)) .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User) .chain(user => Db.find(user.best_friend_id)) // Task(User) --Db.find--> Task(Task(Right(User))) --chain--> Task(Right(User)) .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User)
We only got 'Task(User)' in the end, ready to be forked!
Db.find(4) // Task(Right(User)) .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User) .chain(user => Db.find(user.best_friend_id)) // Task(User) --Db.find--> Task(Task(Right(User))) --chain--> Task(Right(User)) .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User) .fork(console.error, console.log); // { id: 5, name: 'user1', best_friend_id: 6 }
--
const Either = require('data.either'); const Task = require('data.task'); const {List, Map} = require('immutable-ext'); const {Right, Left, fromNullable} = Either; const log = console.log; // F a -> G a // nt(F).map(f) === nt(F.map(f)) //////Exp1///// const res = List(['Hello', 'wolrd']).chain(x => x.split('')); console.log(res.toJS()); // [ 'H', 'e', 'l', 'l', 'o', 'w', 'o', 'l', 'r', 'd' ] //////Exp2///// const first = xs => fromNullable(xs[0]); const largeNumbers = xs => xs.filter(x => x > 100); const doulbe = x => x * 2; // We first run though the whole array to doulbe the number // Then apply first to either transform -> Proformance cost // Since nt(F).map(f) === nt(F.map(f)) const transform1 = xs => first(largeNumbers(xs).map(doulbe)); // Now we get first of array and transform to either, then apply our doulbe function const transform2 = xs => first(largeNumbers(xs)).map(doulbe); const app = xs => transform2(xs); console.log(app([2,400,5,100])); // Either { value: 800 } //////Exp3/////const fake = id => ({id, name: 'user1', best_friend_id: id + 1}); // Db :: find :: id -> Task(Either) const Db = ({ find: id => new Task((rej, res) => { res(id > 2 ? Right(fake(id)): Left('not found')) }) }); const eitherToTask = e => e.fold(Task.rejected, Task.of); Db.find(4) // Task(Right(User)) .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User) .chain(user => Db.find(user.best_friend_id)) // Task(User) --Db.find--> Task(Task(Right(User))) --chain--> Task(Right(User)) .chain(eitherToTask) // Task(Right(User)) --EtT--> Task(Task(User)) --chain--> Task(User) .fork(console.error, console.log); // { id: 5, name: 'user1', best_friend_id: 6 }
【推荐】国内首个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-03-01 [Ramda] Change Object Properties with Ramda Lenses
2017-03-01 [Django] Start a new django project
2017-03-01 [Django] Get started with Django -- Install python and virtualenv
2016-03-01 [Immutable.js] Working with Subsets of an Immutable.js Map()
2016-03-01 [Immutable,js] Iterating Over an Immutable.js Map()
2016-03-01 [Hapi.js] Managing State with Cookies
2016-03-01 [Hapi.js] Request Validation with Joi