-
-
Save Sylvenas/2a06088257344bc1596ed03407194f49 to your computer and use it in GitHub Desktop.
// Task(functor,applicative,monad) | |
const Task = fork => ({ | |
map: f => Task((reject, resolve) => | |
fork(reject, a => resolve(f(a)))), | |
ap: fn => | |
Task((reject, resolve) => fork(reject, a => | |
fn.map(a).fork(reject, resolve) | |
)), | |
chain: f => | |
Task((reject, resolve) => fork(reject, a => | |
f(a).fork(reject, resolve))), | |
fork, | |
[Symbol.for('nodejs.util.inspect.custom')]: () => 'Task(?)' | |
}) | |
// lift | |
Task.of = a => Task((_, resolve) => resolve(a)) | |
// --------------------------async------------ | |
// async get user name | |
const fetchName = Task((_, resolve) => { | |
setTimeout(() => { | |
resolve('Melo') | |
}, 2000) | |
}); | |
// async get user age | |
const fetchAge = Task((_, resolve) => { | |
setTimeout(() => { | |
resolve(24) | |
}, 2000) | |
}); | |
// pure app | |
const app = Task | |
.of(name => age => ({ name, age })) | |
.ap(fetchName) | |
.ap(fetchAge) | |
// effect | |
app.fork(() => { | |
console.log('something went wrong') | |
}, x => { | |
console.log('x', x) // 4 seconds later log {name:'Melo', age:24} | |
}) |
Yes, Promise.then
is almost chain
, because Promise
is almost a monad. But there is the policy in the functional (Haskell) community that demands ap
must have the same semantics as chain
, that is, both must be evaluated in-sequence. For that reason there are two distinct types for async computations, a monadic type with in-sequence semantics, which has an applicative instance with the same semantics. And an applicative type with in-parallel semantics, which has no monad instance, because monads cannot be evaluated in-parallel by design.
However, it is just a policy to make async code more readable. You can mix both types if you want to. Here is a brief dev.to post I wrote about this topic.
Great post! Thanks.
Do you have a plan to put all posts on github? I can do the translation (chinese) work.
I will actually need a Mandarin translation for my course on Github at some point. However, for the time being everything is still in flux, chapters are revised. Translating it makes only sense when things get more stable.
Similar to Promise.all
. But the ap
function only needs to receive one Task.
const Task = fork => ({
ap: fn => Task((reject, resolve) => {
let func, rejected
const firstState = fork(x => {
rejected = true;
return reject(x)
}, x => func = x)
const senondState = fn.fork(x => {
rejected = true;
return reject(x)
}, x => {
if (rejected) return
return resolve(func(x))
})
return [firstState, senondState]
})
})
I think the chain method of monad is more suitable for chain calling, for example:
Applicative(ap) is like
Promise.all
and Monad(chain) is likePromise.then
,I understand it this way, I don’t know it’s correct ?