
On Mon, Mar 29, 2021 at 07:11:49PM +0800, YueCompl via Haskell-Cafe wrote:
But AFAICT, STM composes poorly with other monads in today's mtl fashion, and by itself I don't think effects are tracked sufficiently well.
I'm not aware of an idiomatic way to properly have STM in a monad stack, or is it there?
STM is not a monad transformer, but it is a fine base monad, just like Identity, IO or ST. Here's a contrived example of (StateT Int STM Int): import Control.Concurrent.STM import Control.Monad (when) import Control.Monad.Trans.State.Strict import Control.Monad.Trans.Class (lift) -- main :: IO () main = do tv <- newTVarIO 0 y <- atomically $ flip evalStateT 0 $ do x <- get lift $ do modifyTVar tv (\a -> a + x + 1) y <- readTVar tv when (y > 10) retry return y print y So any or all of RWST work with STM, but you typically want to keep your STM transactions small and simple, so this is not a place where one would generally run wild with fancy stacks that do non-trivial additional computation. Indeed Monad Transformers are not Monads, they're always stacked on top of some base monad. The turtles don't go all the way down. A practical example of STM-like Monad's can be found in Hasql, where database operations run in a Monad that ensures that they have no side-effects that would prevent the transaction from being retried on deadlock detection. This is also a base Monad, where if you like you can stack more (pure) transformers. Which reminds me that ExceptT can be useful in such Monads, which avoid throwing impure exceptions. And is used in Hasql, where the operations tend to be more expensive than in STM, and any overhead from layering ExceptT or similar is quite small. -- Viktor.