【问题标题】:Haskell STM alwaysSucceedsHaskell STM 总是成功的
【发布时间】:2014-09-06 22:28:42
【问题描述】:

haskell 的 stm 库中有一个函数,其类型签名如下:

alwaysSucceeds :: STM a -> STM ()

根据我对 Haskell 中 STM 的理解,在执行 STM 计算时,有三种方式可能会“出错”(松散地使用该术语):

  1. 已读取的 TVar 的值被另一个线程更改。
  2. 违反了用户指定的不变量。这似乎通常通过调用retry 来触发以使其重新开始。这有效地使线程阻塞,然后在读取集中的 TVar 更改后重试。
  3. 引发异常。调用 throwSTM 会导致这种情况。这与前两个不同,因为事务不会重新启动。相反,错误会被传播,或者导致程序崩溃,或者被 IO monad 捕获。

如果这些是准确的(如果不是,请告诉我),我无法理解 alwaysSucceeds 可能会做什么。 always 函数似乎是建立在它之上的,似乎可以在没有 alwaysSucceeds 的情况下编写为:

--This is probably wrong
always :: STM Bool -> STM ()
always stmBool = stmBool >>= check

alwaysSucceeds 的文档说:

alwaysSucceeds 添加一个新的不变量,当传递给 alwaysSucceeds,在当前事务结束时,在结束时 以后的每一笔交易。如果它在任何这些点失败 然后违反它的事务被中止并引发异常 由不变量传播。

但由于参数是STM a 类型(a 中的多态),它不能使用事务返回的值用于决策的任何部分。因此,它似乎会寻找我之前列出的不同类型的故障。但那有什么意义呢? STM monad 已经处理了失败。将它包装在这个函数中会如何影响它?还有为什么a类型的变量会被丢弃,导致STM ()

【问题讨论】:

标签: haskell ghc stm


【解决方案1】:

alwaysSucceeds 的特殊效果不是它如何在运行时检查故障(单独运行“不变”动作应该做同样的事情),而是它如何重新运行不变量在交易结束时检查。

基本上,这个函数创建一个用户指定的不变量,如上面的(2),它不仅要保持现在,还要在以后的事务结束时保持。

请注意,“事务”并不是指STM monad 中的每个子操作,而是指传递给atomically 的组合操作。

我猜a 只是为了方便而被删除,因此您不必在将操作传递给alwaysSucceeds 之前将其转换为STM ()(例如使用void)。无论如何,返回值对于后面的重复检查将毫无用处。

【讨论】:

  • 知道了。真实世界的 haskell 教程第 28 章给出了一个很好的例子。所以看起来,如果我理解正确的话,alwaysSucceedsalways 都应该在有可能调用throwSTM 的东西上调用。似乎使用其中很多会显着影响性能,因为它们会在每次事务后重新运行。
  • @AndrewThaddeusMartin 经过一些进一步的测试,我不认为它必须是throwSTM,它似乎可以基于retry(包括check),尽管如果你的不变量那么失败,您的事务将无限期重试,除非您在另一个线程中有某些东西导致它最终成功。
  • @AndrewThaddeusMartin 一个有启发性的测试是:do t <- newTVarIO 0; atomically . alwaysSucceeds $ readTVar t >>= check . (>= 0); forkIO $ forever (do threadDelay 10000000; atomically $ modifyTVar t (max 1)); replicateM_ 3 . atomically . modifyTVar t $ subtract 1
  • 我进行了测试。聪明的你,30秒后终于成功了,有道理。尽管我觉得这种使用方式总是显得有些奇怪,并且使推理更加困难。此外,您可以编写像 f = alwaysSucceeds . alwaysSucceeds . alwaysSucceeds 这样的函数,这看起来有点奇怪。感觉结果在 IO monad 中对alwaysSucceeds 更有意义。
猜你喜欢
  • 1970-01-01
  • 2019-06-06
  • 1970-01-01
  • 1970-01-01
  • 2020-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多