I use this trick, that unlike the previous workarounds permits concurrency and does handle the IO computation in a civilized way, that is, it permits brackets and so on:
notReallySafeButNotAsUnsafeAsUnsafeIOToSTM= safeIOToSTM
safeIOToSTM ∷ IO a → STM a
safeIOToSTM req= unsafeIOToSTM $ do
tv ← newEmptyMVar
forkIO $ (req ↠ putMVar tv . Right)
`Control.Exception.catch`
(λ(e ∷ SomeException) → putMVar tv $ Left e )
r ← takeMVar tv
case r of
Right x → return x
Left e → throw e
Here the IO computation is run in another thread. Even If the STM transaction is aborted, the IO computation finalizes. If the STM transaction is retried, the IO computation is re-executed, so it is up to you to take this into account depending on what you intend to do.
If the IO computation throws an exception the STM transaction throws it. As far as I remember, this trick was used in a package time ago to do solve the problem.