How to use applicative for concurrency?

This is a continuation of my previous question . I copied the example below from Haxl

Suppose I retrieve data from a blog server to display a blog page that contains recent posts, popular posts, and message topics.

I have the following API for data entry:

val getRecent : Server => Seq[Post] = ... val getPopular : Server => Seq[Post] = ... val getTopics : Server => Seq[Topic] = ... 

Now I need to compose them to implement the new getPageData function

 val getPageData: Server => (Seq[Post], Seq[Post], Seq[Topic]) 

Haxl suggests using the new Fetch monad to make the API composite.

 val getRecent : Fetch[Seq[Posts]] = ... val getPopular : Fetch[Seq[Posts]] = ... val getTopics : Fetch[Seq[Topic]] = ... 

Now I can define my getPageData: Fetch[A] with a monadic composition

 val getPageData = for { recent <- getRecent popular <- getPopular topics <- getTopics } yield (recent, popular, topics) 

but it does not start getRecent , getPopular and getTopics at the same time.

Haxl suggests using the applicative composition <*> to create "parallel" functions (that is, functions that can be executed simultaneously). So my questions are:

  • How to implement getPageData Assuming Fetch[A] is Applicative ?
  • How to implement Fetch as Applicative but not Monad ?
+5
source share
1 answer

How to implement getPageData, assuming Fetch [A] is applicative?

All we need to do is reset monadic binding >>= in favor of the applicative <*> . Therefore, instead of

 val getPageData = for { recent <- getRecent popular <- getPopular topics <- getTopics } yield (recent, popular, topics) 

we will write something like (in Haskell syntax, sorry, I can not make Scala from the top of my head):

 getPageData = makeTriple <$> getRecent <*> getPopular <*> getTopics where makeTriple xyz = (x, y, z) 

But whether this can have the desired effect depends on the second question!

How to implement Fetch as applicative, but not Monad?

The key difference between monadic and applicative sequencing is that monadic can depend on the value inside the monadic value, while the applicative <*> cannot. Notice how the monadic expression for getPageData above binds recent and popular names before reaching getTopics . These names could be used to change the structure of an expression, for example, by getting a different data source if recent empty. But with an applicative expression, the results of getRecent and getPopular are not factors in the structure of the expression itself. This property allows us to smooth each term in the applicative expression at the same time, because we know the structure of the expression statically.

So, using the above observation and, obviously, the specific form of the Fetch data type, we can find 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 $ fx 

For comparison, suppose we tried to make monadic binding with simultaneous 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 $ fx 
+4
source

Source: https://habr.com/ru/post/1210212/


All Articles