Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell STM alwaysSucceeds

Tags:

haskell

ghc

stm

There is a function in haskell's stm library with the following type signature:

alwaysSucceeds :: STM a -> STM ()

From what I understand of STM in haskell, there are three ways that something can "go wrong" (using that term loosely) while an STM computation is executing:

  1. The value of an TVar that has been read is changed by another thread.
  2. An user-specified invariant is violated. This seems to usually be triggered by calling retry to make it start over. This effectively makes the thread block and then retry once a TVar in the read set changes.
  3. An exception is thrown. Calling throwSTM causes this. This one differs from the first two because the transaction doesn't get restarted. Instead, the error is propagated and either crashes the program or is caught in the IO monad.

If these are accurate (and if they are not, please tell me), I can't understand what alwaysSucceeds could possibly do. The always function, which appears to be built on top of it, seems like it could be written without alwaysSucceeds as:

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

The documentation for alwaysSucceeds says:

alwaysSucceeds adds a new invariant that must be true when passed to alwaysSucceeds, at the end of the current transaction, and at the end of every subsequent transaction. If it fails at any of those points then the transaction violating it is aborted and the exception raised by the invariant is propagated.

But since the argument is of type STM a (polymorphic in a), it can't use the value that the transaction returns for any part of the decision making. So, it seems like it would be looking for the different types of failures that I listed earlier. But what's the point of that? The STM monad already handles the failures. How would wrapping it in this function affect it? And why does the variable of type a get dropped, resulting in STM ()?

like image 493
Andrew Thaddeus Martin Avatar asked Sep 06 '14 22:09

Andrew Thaddeus Martin


1 Answers

The special effect of alwaysSucceeds is not how it checks for failure at the spot it's run (running the "invariant" action alone should do the same thing), but how it reruns the invariant check at the end of transactions.

Basically, this function creates a user-specified invariant as in (2) above, that has to hold not just right now, but also at the end of later transactions.

Note that a "transaction" doesn't refer to every single subaction in the STM monad, but to a combined action that is passed to atomically.

I guess the a is dropped just for convenience so you don't have to convert an action to STM () (e.g. with void) before passing it to alwaysSucceeds. The return value will be useless anyhow for the later repeated checks.

like image 97
Ørjan Johansen Avatar answered Sep 20 '22 12:09

Ørjan Johansen