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
. Возвращаемое значение будет бесполезным для последующих повторных проверок.