Polysemy: AtomicState
One of the effect used over and over again in our previous example is State
, however, it's lack of atomicity can cause problems:
incHundred =
forM_ ([0 .. 99] :: [Int]) $ \_ ->
modify' @Int (+ 1)
incThousand =
void $ sequenceConcurrently $ map (const incHundred) ([0 .. 9] :: [Int])
You might not get a 1000
here.
That why AtomicState
is useful:
data AtomicState s m a where
AtomicState :: (s -> (s, a)) -> AtomicState s m a
AtomicGet :: AtomicState s m s
We can rewrite your code:
incHundred' =
forM_ ([0 .. 99] :: [Int]) $ \_ ->
atomicModify' @Int (+ 1)
incThousand' =
void $ sequenceConcurrently $ map (const incHundred') ([0 .. 9] :: [Int])
Not a big change.
Basically, AtomicState
provides interpreters that rely on atomic operations (runAtomicStateIORef
and atomicStateToIO
over IORef
and runAtomicStateTVar
over TVar
).
However, beware that other interpreters (such as atomicStateToState
) rely on State
and break the behavior.
See the full the code here.