【问题标题】:Haskell - Is effect order deterministic in case of Applicative?Haskell - 在 Applicative 的情况下,效果顺序是否确定?
【发布时间】:2013-01-10 13:36:43
【问题描述】:

在执行someFun <$> (a :: IO ()) <$> (b :: IO ())定义的IO动作时,ab动作的执行是否有序?也就是说,我可以指望ab 之前执行吗?

对于 GHC,我可以看到 IO 是使用 State 实现的,也可以看到 here 它是一个 Applicative 实例,但找不到实际实例声明的来源。通过 State 实现表明不同的 IO 效果需要是顺序的,但不需要定义它们的顺序。

在 GHCi 中玩耍似乎 Appliative 保留了效果顺序,但这是一些普遍的保证,还是 GHC 特定的?我会对细节感兴趣。

import System.Time
import Control.Concurrent
import Data.Traversable
let prec (TOD a b) = b
fmap (map prec) (sequenceA $ replicate 5 (threadDelay 1000 >> getClockTime))

[641934000000,642934000000,643934000000,644934000000,645934000000]

谢谢!

【问题讨论】:

标签: haskell applicative


【解决方案1】:

肯定是确定性,是的。对于任何特定实例,它总是会做同样的事情。但是,没有固有的理由选择从左到右而不是从右到左来选择效果顺序。

但是,来自the documentation for Applicative

如果f 也是Monad,它应该满足pure = return(<*>) = ap(这意味着pure<*> 满足应用函子定律)。

ap的定义是这样的,来自Control.Monad

ap :: (Monad m) => m (a -> b) -> m a -> m b
ap =  liftM2 id

liftM2 的定义很明显:

liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }

这意味着,对于任何作为 MonadApplicative 的函子,预计(根据规范,因为这不能在代码中强制执行),Applicative 将从左到右工作,以便liftM2 中的do 块与liftA2 f x y = f &lt;$&gt; x &lt;*&gt; y 执行相同的操作。

由于上述原因,即使对于没有对应 MonadApplicative 实例,按照惯例,效果通常也是从左到右排序的。

更广泛地说,因为Applicative 计算的结构必须独立于“效果”,所以您通常可以独立于Applicative 效果的排序方式来分析程序的含义。例如,如果 [] 的实例更改为从右到左的顺序,则使用它的任何代码都会给出相同的结果,只是列表元素的顺序不同。

【讨论】:

  • 谢谢!我是否正确推断出如果效果顺序对我很重要并且我想成为理论上的,那么我应该使用 Monad,但如果我是实用的,那么 Applicative 将(通常,小心)适合?
  • @ron:在实践中,您可以简单地假设 Applicative 实例将按从左到右的顺序排列,除非它们大声宣布相反,就像 Backwards 包装器一样。请注意Monad 实例也可能在其他方面不明确——例如,反转的State monad 将状态值向后线程化,但类型本身与常规State 相同。再举一个例子,[] 有两个可能的Applicative 实例,即使在排序顺序之外。您无法避免在一定程度上依赖文档中的内容。
  • @C.A.McCann "[...] 您通常可以独立于 Applicative 效果的排序方式来分析程序的含义。" 为什么?这不只适用于 commutative 应用函子吗?通常,重新排序 Applicative 效果可以显着改变结果。下面是一个示例:myParser = (++) &lt;$&gt; foo &lt;*&gt; bar 其中foobar 是parsec Parser String。如果我将排序更改为从右到左,myParser 将在输入字符串"foobar" 上失败。在这种情况下,您不同意效果的重新排序影响了程序的含义吗?
【解决方案2】:

是的,顺序是由 Monad-Applicative 对应预定义的。这很容易看出:(*&gt;) 组合器需要与行为良好的 Applicative monad 实例中的 (&gt;&gt;) 组合器相对应,其定义为:

a *> b = liftA2 (const id) a b

换句话说,如果ba 之前执行,Applicative 实例将表现不佳。

编辑:附带说明:这没有在任何地方明确指定,但您可以找到许多其他类似的对应关系,例如 liftM2 = liftA2 等。

【讨论】:

    【解决方案3】:

    对于 IO Applicative,肯定是这样。但请查看 async package 以获取 Applicative 的示例,其中在 f &lt;$&gt; a &lt;*&gt; b 中,ab 的效果并行发生。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-08
      • 1970-01-01
      • 2012-02-23
      • 1970-01-01
      • 1970-01-01
      • 2015-10-26
      • 1970-01-01
      相关资源
      最近更新 更多