Haskell STM всегда превосходит

В библиотеке haskell stm есть функция со следующей сигнатурой типа:

alwaysSucceeds :: STM a -> STM ()

Из того, что я понимаю в STM в haskell, есть три способа, что что-то может "пойти не так" (используя этот термин свободно), в то время как выполняется STM-вычисление:

  • Значение прочитанного TVar изменяется другим потоком.
  • Нарушен пользовательский инвариант. Обычно это срабатывает, вызывая retry, чтобы начать работу. Это эффективно блокирует поток, а затем повторяет попытку после изменения TVar в считываемом наборе.
  • Вызывается исключение. Вызов throwSTM вызывает это. Этот отличается от первых двух, потому что транзакция не перезапускается. Вместо этого ошибка распространяется и либо сбой программы, либо попадает в IO-монаду.

Если они точны (а если нет, скажите, пожалуйста), я не могу понять, что может сделать alwaysSucceeds. Функция always, которая, по-видимому, построена поверх нее, кажется, что она может быть записана без alwaysSucceeds как:

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

В документации для alwaysSucceeds указано:

alwaysSucceeds добавляет новый инвариант, который должен быть истинным при передаче alwaysSucceeds, в конце текущей транзакции и в конце каждой последующей транзакции. Если он терпит неудачу в любой из этих точек то транзакция, нарушающая ее, прерывается, и исключение возникает по инварианту.

Но поскольку аргумент имеет тип STM a (полиморфный в a), он не может использовать значение, которое транзакция возвращает для любой части принятия решения. Таким образом, похоже, что он будет искать различные типы сбоев, которые я перечислял ранее. Но в чем смысл? Монада STM уже обрабатывает сбои. Как его обертывание в этой функции повлияет? И почему переменная типа a отбрасывается, что приводит к STM ()?

Ответ 1

Особый эффект alwaysSucceeds заключается не в том, как он проверяет наличие сбоя на месте, в котором он запускается (только одно действие "инвариантное" должно делать то же самое), но как он повторяет проверку инварианта в конце транзакций.

В принципе, эта функция создает указанный пользователем инвариант, как в (2) выше, который должен выполняться не только прямо сейчас, но и в конце последующих транзакций.

Обратите внимание, что "транзакция" не относится к каждому отдельному субакту в монаде STM, а к объединенному действию, которое передается в atomically.

Я думаю, что a отбрасывается только для удобства, поэтому вам не нужно преобразовывать действие в STM () (например, с void), прежде чем передавать его на alwaysSucceeds. Возвращаемое значение будет бесполезным для последующих повторных проверок.