[Compose] 20. Principled type conversions with Natural Transformations

Natural Transformations, let's explain it by using a coding example, for example, we have a 'Either' holding a value 'a' and we want to transform a' Task' that holding an 'a'.

// Either(a) -> Task(a)

Let's start coding:

复制代码
const Either = require('data.either');
const {Right, Left, fromNullable} = Either;
const Task = require('data.task');

const eitherToTask = e =>
    e.fold(Task.rejected, Task.of);

eitherToTask(Right('Good')).fork(
    e => console.error('err', e),
    x => console.log('res', a)
) // 'res' Good
复制代码

 

Let's go thought:

const eitherToTask = e => 
    e.fold(Task.rejected, Task.of);

'Either' has 'fold' method (left, right), as we know 'fold' will unbox the contianer just return the value, so that it will unbox the 'Either' type, and we wrap the return value with 'Task'. So now, 'eitherToTask' return a 'Task'

 

eitherToTask(Right('Good')).fork(...)

The result we using 'Right' instead of 'Left' we will explain later, but here, we know we have a 'Task', therefore we can call 'fork' to trigger the side effect.

 

Law:

natural_transform(Functor).map(function) === natural_transform(Functor.map(function))

On the left side of equals, we have natural transform function wraps a Functor, then map to a function.

On the right side, we have a natural transform function wrap functor that map to a funciton.

Let's see an example:

复制代码
const Box = x => ({
    map: f => Box(f(x)),
    fold: f => f(x)
})

const boxToEither = b => 
    b.fold(Right);

const res1 = boxToEither(Box(100)).map(x => x * 2);
console.log(res1);  // Either (200)
const res2 = boxToEither(Box(100).map(x => x * 2));
console.log(res2);  // Either (200)
复制代码

 

What if we using 'Left' instead of 'Right':

复制代码
const Box = x => ({
    map: f => Box(f(x)),
    fold: f => f(x)
})

const boxToEither = b => 
    b.fold(Left);

const res1 = boxToEither(Box(100)).map(x => x * 2);
console.log(res1);  // Either(100)
const res2 = boxToEither(Box(100).map(x => x * 2));
console.log(res2);  // Either(200)
复制代码

As we can see 'res1' is no longer equals to 'res2', because Left will ingore mapping function. 

 

Another example: List -> Either:

// first :: [] -> Either
const first = xs => fromNullable(xs[0]);
const res3 = first([1,2,3]).map(x => x +1);
const res4 = first([1,2,3].map(x => x +1));
 console.log(res3);  // Either(2)
 console.log(res4); // Either(2)

 

These two shall be equal and they are. Any function that satisfies this equation is a natural transformation. Let's look at this on the board here.

 

If we have some F(a) and some functor holding an a and we map(f) over it, it transforms that a to a b. we're just mapping a function from the type a to some type b here all inside our functor f. Then we run a natural transformation we'll have a G(b).

If we take the other path moving downward we'll first naturally transform our functor holding an a into the G(a) here, and then we map(f) over that to get a G(b). We end up with the same result. This can be quite useful.

posted @   Zhentiw  阅读(261)  评论(0编辑  收藏  举报
编辑推荐:
· 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
点击右上角即可分享
微信分享提示