如何使用Applicative进行并发?

Mic*_*ael 6 concurrency functional-programming scala applicative haxl

这是我上一个问题的后续行动.我从Haxl复制了下面的例子

假设我从博客服务器获取数据以呈现博客页面,其中包含最近的帖子,热门帖子和帖子主题.

我有以下数据提取API:

val getRecent  : Server => Seq[Post] = ...
val getPopular : Server => Seq[Post] = ...
val getTopics  : Server => Seq[Topic] = ...
Run Code Online (Sandbox Code Playgroud)

现在我需要编写它们来实现一个新功能 getPageData

val getPageData: Server => (Seq[Post],  Seq[Post], Seq[Topic])
Run Code Online (Sandbox Code Playgroud)

Haxl建议使用新的monad Fetch来使API可组合.

val getRecent  : Fetch[Seq[Posts]] = ...
val getPopular : Fetch[Seq[Posts]] = ...
val getTopics  : Fetch[Seq[Topic]] = ...
Run Code Online (Sandbox Code Playgroud)

现在我可以getPageData: Fetch[A]monadic组合来定义我

val getPageData = for {
  recent  <- getRecent
  popular <- getPopular
  topics  <- getTopics
} yield (recent, popular, topics)
Run Code Online (Sandbox Code Playgroud)

但它不运行getRecent,getPopular以及getTopics兼任.

Haxl建议使用applicative composition <*>来组成"并发"函数(即可以并发运行的函数).所以我的问题是:

  • 如何实现getPageData假设Fetch[A]是一个Applicative
  • 如何实现Fetch作为Applicative,但不是Monad

Ale*_*eth 4

假设 Fetch[A] 是 Applicative 如何实现 getPageData ?

我们需要做的就是放弃单子绑定,>>=转而使用应用程序<*>。所以而不是

val getPageData = for {
  recent  <- getRecent
  popular <- getPopular
  topics  <- getTopics
} yield (recent, popular, topics)
Run Code Online (Sandbox Code Playgroud)

我们会写类似的东西(用 Haskell 语法;抱歉,我不能凭空想象 Scala):

getPageData = makeTriple <$> getRecent <*> getPopular <*> getTopics
  where
    makeTriple x y z = (x, y, z)
Run Code Online (Sandbox Code Playgroud)

但能否达到预期的效果,就要看第二个问题了!

如何将 Fetch 实现为 Applicative 而不是 Monad ?

一元排序和应用排序之间的主要区别在于,一元排序可以依赖于一元值内的值,而应用排序则<*>不能。请注意上面的一元表达式如何在到达 之前getPageData绑定名称recent和。这些名称用于更改表达式的结构,例如,在 case为空时获取其他数据源。但对于应用表达式,和的结果并不是表达式本身结构中的因素。此属性允许我们同时触发应用表达式中的每个术语,因为我们静态地知道表达式的结构。populargetTopicsrecentgetRecentgetPopular

因此,利用上面的观察结果,以及 Fetch 数据类型的特定形状,我们可以为 给出一个合适的定义<*>。我认为以下说明了总体思路:

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
Run Code Online (Sandbox Code Playgroud)

为了进行比较,假设我们尝试使用并发求值进行单子绑定:

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
Run Code Online (Sandbox Code Playgroud)