How to implement getPageData assuming Fetch[A] is an Applicative ?
All we need to do is drop the monadic bind >>=
in favour of the applicative <*>
. So instead of
val getPageData = for {
recent <- getRecent
popular <- getPopular
topics <- getTopics
} yield (recent, popular, topics)
we would write something like (in Haskell syntax; sorry, I can't do Scala off the top of my head):
getPageData = makeTriple <$> getRecent <*> getPopular <*> getTopics
where
makeTriple x y z = (x, y, z)
But whether this has the desired effect is contingent upon the second question!
How to implement Fetch as an Applicative but not a Monad ?
The key distinction between monadic and applicative sequencing is that the monadic one can depend upon the value inside a monadic value, whereas the applicative <*>
cannot. Notice how the monadic expression for getPageData
above binds the names recent
and popular
before reaching getTopics
. Those names could have been used to change the structure of the expression, for example by getting some other data source in case recent
is empty. But with the applicative expression, the results of getRecent
and getPopular
are not factors in the structure of the expression itself. This property allows us to fire off each term in the applicative expression concurrently, because we know the structure of the expression statically.
So, using the observation above, and obviously the particular shape of the Fetch datatype, we can come up with a suitable definition for <*>
. I think the following illustrates the general idea:
data Fetch a = Fetch { runFetch :: IO a }
fetchF <*> fetchX = Fetch $ do
-- Fire off both IOs concurrently.
resultF <- async $ runFetch fetchF
resultX <- async $ runFetch fetchX
-- Wait for both results to be ready.
f <- wait resultF
x <- wait resultX
return $ f x
For comparison, suppose we tried to do monadic bind with concurrent evaluation:
fetchF >>= fetchK = Fetch $ do
resultF <- async $ runFetch fetchF
-- Oh no, we need resultF in order to produce the next
-- Fetch value! We just have to wait...
f <- wait resultF
fetchX <- async $ runFetch (fetchK f)
x <- wait $ runFetch fetchX
return $ f x