A question on Async and applicatives
See original GitHub issueI am going to outline the nesting problem with promises and how I imagined they would be solved with crocks in this issue
This is my honest process in trying to find a solution. The answer may simply be to use do
/async await
.
Here is an example of promise nesting taken from https://gist.github.com/MaiaVictor/bc0c02b6d1fbc7e3dbae838fb1376c80
function getBalances() {
return web3.eth.accounts()
.then(accounts => Promise.all(accounts.map(web3.eth.getBalance).then(balances => [accounts, balances])))
.then(([accounts, balances]) => Ramda.zipObject(accounts, balances));
}
When I saw this I first thought that crocks could provide an elegant solution with applicative like this:
Async.fromPromise(web3.eth.accounts)
.ap(Async.all(map(Async.fromPromise(web3.eth.getBalance))))
.fork(console.error, Ramda.zipObject)
Where applicative provides the current data in the flow and applicative adds new data to the chain.
But applicatives don’t work like this, so my question is:
Is there simply no easy way to avoid nested data calls for use cases when you wish to add extra data to the flow?
Issue Analytics
- State:
- Created 5 years ago
- Comments:7 (5 by maintainers)
Top Results From Across the Web
Can Applicative Functors make async Tasks more effective?
I don't know Folktale's Task but usually the applicative in the context of asynchronous computations runs "in parallel", because the next ...
Read more >Interview question: async & await (C#) - DEV Community
Q: What is the purpose of async / await keywords? These keywords allow writing asynchronous non-blocking code in a synchronous fashion.
Read more >Chapter 13. Working with asynchronous computations
Using Task to represent asynchronous computations; Composing asynchronous operations sequentially and in parallel; Traversables: handling lists of elevated ...
Read more >What are these applicative functors you speak of? | Devlog
They are asynchronous, so you might be tempted to use Async/await but that wouldn't be the best idea. Those two don't depend on...
Read more >async: Asynchronous and Concurrent Programming
Exercise: what will this program output? The Concurrently newtype wrapper uses the concurrently function to implement its Applicative instance, and race to ...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
So the best way to solve a problem like this is chunk by chunk. You were 💯 that the solution lies around
applicative
, just in (2) different ways then you may have been thinking of. I will break down a solution to this problem step by step.Before we start with the code we will bring some stuff in from
crocks
:So our Promise functions need to be 🌽verted to
Async
functions…Also going tobind
incase that dep uses internal state (aws and many others need to be bound):At the heart of the problem we need to retain a some data for use later. Anytime you have parallel paths (one to process and one to retain (or process in another way if you wanna)), you can think of Product Types, or in this case the humble
Pair
.Instead of working directly with a
Pair
, because we want to share a value between (2) paths, we can use a function calledfanout
. This will take a value and send it to (2) provided functions and return their results in a newPair
. ThatgetBalance
will return anAsync
so we want theString
on the left lifted into anAsync
as is, using ourasyncOf
function. It is important that it is on the left, as we will be usingobjOf
which takes thekey
on the left. Doing the following and passing aString
will give back aPair
withAsync
s in both sides key on the left, value on the right:Now that we have our
Pair
ofAsync
we can use the Applicative to combine them under an operation usingliftA2
that will lift a function into anAsync
(in this case) and apply bothAsync
to the function, returning a newAsync
that is the result. In this case that function isobjOf
as it will make a new object with the accountId as the key and the balance as the value.BUT we need to take them out of the
Pair
and give them toliftA2
. That is wheremerge
comes into play. It takes a binary function (liftA2(objOf)
) and applies the left side of thePair
as the first argument and the right side as the second:Now that those online, we can build a composition that gives us a function of the form
String -> Async Error Object
that will build a balance for (1) account:Perfect! The only bummer is that it only works for one. So another way we can use the
Async
as an Applicative is to take advantage ofArray
beingtraversable
. BecausegetAccounts
returns an Array of Strings ([ String ]
), we can just chain in atraverse
with theaccountBalance
function and get something like:Now we are almost there, that will return an
Array
of Balance Objects, looks like you want one object as the result, so we can use theAssign
Monoid withmreduce
(which extracts to a value of the carrier type of a given Monoid. with our final flow being:With it all together, it looks like this:
Although it seems like a lot, there is a lot of potential for reusability throughout your app. each step is in its own function which can be used in other places and not in a nested Promise.
Hope this helps…Maybe I should do a live code on using Applicatives in this fashion?
Awesome. Closing!