scala - 如何使用Applicative实现并发?

标签 scala concurrency functional-programming applicative haxl

这是我之前的 question 的后续内容。我从 Haxl 复制了下面的示例

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

我有以下数据获取 API:

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

现在我需要组合它们来实现一个新功能 getPageData

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

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

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

现在我可以定义我的 getPageData: Fetch[A]具有一元组合

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

但它不运行getRecent , getPopular ,和getTopics同时。

Haxl建议使用应用组合 <*>组成“并发”函数(即可以同时运行的函数)。所以我的问题是:

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

最佳答案

How to implement getPageData assuming Fetch[A] is an Applicative ?

我们需要做的就是删除单子(monad)绑定(bind) >>=支持应用<*> 。所以而不是

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

我们会写一些类似的东西(用 Haskell 语法;抱歉,我无法立即使用 Scala):

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

但是这是否能达到预期的效果还要看第二个问题!

How to implement Fetch as an Applicative but not a Monad ?

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

因此,利用上面的观察结果,以及 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

为了进行比较,假设我们尝试使用并发评估进行单子(monad)绑定(bind):

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

关于scala - 如何使用Applicative实现并发?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27730707/

相关文章:

scala - 我将如何对内部随机排列数组的方法进行单元测试

javascript - 你能给我一个如何使用 Ramda lift 的例子吗?

c# - 具有动态 maxCount 的 SemaphoreSlim

c++ - std::atomic 和多核处理器

list - 向左和向右折叠无限列表

java - 回溯没有可变成员的树

scala - 模拟包含抽象 val 成员的 Scala 特征

scala - 如何清除Scala REPL中的所有变量

java/scala : Regex for skipping odd number of backslash while splitting a String?

c - 在 C 中使用清理处理程序安全取消 pthread