Haskell STM всегда успешен

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

alwaysSucceeds :: STM a -> STM ()

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

  1. Значение прочитанного TVar изменяется другим потоком.
  2. Указанный пользователем инвариант нарушен. Обычно это вызывается вызовом retry, чтобы начать сначала. Это эффективно блокирует поток, а затем повторяет попытку после изменения TVar в наборе чтения.
  3. Выбрасывается исключение. Вызов 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 ()?


person Andrew Thaddeus Martin    schedule 06.09.2014    source источник


Ответы (1)


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

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

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

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

person Ørjan Johansen    schedule 07.09.2014
comment
Понятно. Глава 28 учебника по Haskell из реального мира дает довольно хороший пример этого. Итак, похоже, если я правильно понимаю, alwaysSucceeds и always должны вызываться для чего-то, что имеет возможность вызова throwSTM. Также кажется, что использование многих из них значительно повлияет на производительность, поскольку они перезапускаются после каждой транзакции. - person Andrew Thaddeus Martin; 08.09.2014
comment
@AndrewThaddeusMartin После некоторого дальнейшего тестирования я не думаю, что это должно быть throwSTM, кажется, что оно может быть основано на retry (включая check), хотя, если ваш инвариант затем даст сбой, ваша транзакция будет повторяться бесконечно, если у вас нет чего-то в другом потоке это приводит к тому, что в конечном итоге это удается. - person Ørjan Johansen; 08.09.2014
comment
@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 - person Ørjan Johansen; 08.09.2014
comment
Я провел тест. Умница с твоей стороны, это, наконец, удается через 30 секунд, что имеет смысл. Хотя мне кажется, что такой способ использования всегда кажется немного странным и усложняет рассуждения. Кроме того, вы можете написать такие функции, как f = alwaysSucceeds . alwaysSucceeds . alwaysSucceeds, что кажется довольно странным. Похоже, что результат, находящийся в монаде IO, вместо этого имел бы больше смысла для alwaysSucceeds. - person Andrew Thaddeus Martin; 09.09.2014