自然变换
所谓自然变换也也就是变化包裹值的容器盒子:F(x) => G(x)
,举个简单的例子,把Either转换为Task:
const eitherToTask = e =>
e.fold(Task.rejected, Task.of)
const res = eitherToTask(Right('hello'))
.fork(err => { console.error('err', err) }, x => console.log('res', x)) // res hello
const res2 = eitherToTask(Left('errrrrr'))
.fork(err => { console.error('err', err) }, x => console.log('res', x)) // err errrrrr
同构
前面我们介绍过Task 是一种Lazy Promise的概念,那么是否可以将Task转为promise呢?答案是肯定的!
// taskToPromise :: Task a b -> Promise a b
const taskToPromise = x => new Promise((resolve, reject) => x.fork(reject, resolve));
const task = Task((rej, res) => {
setTimeout(() => res('hello taskToPromise'), 200)
})
const res = taskToPromise(task)
.then(x => { console.log(x) }, x => console.log('something went wrong')) // => hello taskToPromise
console.log(res) // => Promise { <pending> }
同样的,我们也可以把Promise转换为Task
const promiseToTask = p => Task((reject, resolve) => p.then(resolve).catch(reject));
const promise = Promise.resolve('hello promiseToTask')
const res = promiseToTask(promise)
.fork(
err => console.log('something went wrong'),
x => console.log(x)) // => hello promiseToTask
**Note:**我们没办法实现taskToEither
,因为我们不能把一个异步的逻辑转换为同步的过程,这个是不合理的,因为异步的结果,必须要等到异步call back的时候才能拿到。
定律
nth(fx).map(f) == nt(fx.map(f))
也就是先进行map然后自然变换和先自然变化然后map的结果是一样的
// 因为nt必须满足这个定律所以boxToEither必须使用Right,因为left会跳过map
const boxToEither = b =>
b.fold(Right)
const res = boxToEither(Box(200)).map(x => x * 2)
console.log(res) // Right(400)
const res2 = boxToEither(Box(200).map(x => x * 2))
console.log(res2) // Right(400)
自然变换的目的
是为了函数组合,其实我们目前所做的所有努力都是为了让函数组合更方便,是想如果自然变换,我们怎么进行不同容器的chain和map呢?
const fake = id =>
({ id, name: `user${id}`, best_friend_id: id + 1 })
const Db = ({
find: id =>
Task((rej, res) =>
res(id > 2 ? Right(fake(id)) : Left('not found')))
})
const eitherToTask = e =>
e.fold(Task.rejected, Task.of)
const app = id => Db.find(id) // Task(Right(user))
.chain(eitherToTask)
.chain(user => Db.find(user.best_friend_id))
.chain(eitherToTask)
app(3).fork(console.error, console.log) // { id: 4, name: 'user4', best_friend_id: 5 }
app(2).fork(console.error, console.log) //not found