
Simon just pointed out to me that this is quite closely related, though more general: http://hackage.haskell.org/package/stm-io-hooks http://www.haskell.org/haskellwiki/New_monads/MonadAdvSTM Cheers, Simon On 24/02/2010 09:58, Simon Marlow wrote:
John Lauchbury has proposed the following addition to Control.Concurrent.STM. John's proposal is below, comments welcome.
Personally I'm in two minds about this. It certainly captures a useful idiom, but it fails the "don't name compositions" test, and one of the additions is just a renaming of 'return', which makes it hard to justify.
Cheers, Simon
----- I find I often write STM code with the structure:
do ... IO code ... v <- atomically $ do ... STM code ... if ..... then do ... STM code ... return (Left ...) else do ... STM code ... return (Right ...) case v of Left ... -> ... IO actions ... Right ... -> ... IO actions ...
where I have to communicate the result of choices inside STM code to corresponding code in the outer IO monad. This means that two separate parts of the code have to be kept in sync. In this simple case it's merely irritating. With larger and more complex structures it can be quite tricky to keep right.
Now I use the functions 'atomicallyIO' and 'afterCommit', and can structure the code as
do ... IO code ... v <- atomicallyIO $ do ... STM code ... if ..... then do ... STM code ... afterCommit $ ... IO actions ... else do ... STM code ... afterCommit $ ... IO actions ...
As you see, it allows the IO code to be placed in the appropriate part of the STM code, ready to be executed when that STM branch is committed.
Here's the code to would be added to the STM module:
---------------------------------------------------------------------- -- 'atomicallyIO' and 'afterCommit' are useful syntactic sugar for a -- simple continuation paradigm. They allow IO code to be placed within -- the same block of STM code in which control decisions are made -- (avoiding the need to communicate those decisions with a data type -- sent to a subsequent matching case).
atomicallyIO :: STM (IO a) -> IO a atomicallyIO ioSTM = join $ atomically ioSTM
afterCommit :: IO a -> STM (IO a) afterCommit mIO = return mIO ----------------------------------------------------------------------
Obviously the code is very simple, but I still find it useful to name the paradigm, and to have the specific type declarations to catch errors.