Mic*_*ael 6 concurrency functional-programming scala applicative 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?假设 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)