
Hello All, I have recently encountered 2 situations where I needed an IO action, but only had a monad stack with IO at the bottom. The two examples were: 1. from Control.Concurrent.Async withAsync :: IO a -> (Async a -> IO b) -> IO b 2. from Network.WebSockets runClient :: String -- ^ Host -> Int -- ^ Port -> String -- ^ Path -> (Connection -> IO a) -- ^ Client application -> IO a I need to pass a function that returns an IO action to both these functions. I think my life would be easier if the function signatures were: 1. withAsync :: MonadIO mIO => mIO a -> (Async a -> mIO b) -> mIO b 2. from Network.WebSockets runClient :: MonadIO mIO => -> String -- ^ Host -> Int -- ^ Port -> String -- ^ Path -> (Connection -> mIO a) -- ^ Client application -> mIO a There are many other examples, a notable one are the functions in Control.Exception also always expect an IO action. I know we have libraries to solve this problem, such as lifted-async, lifted-base and the functionality in Control.Monad.Trans.Control. But what are the best practices for writing code that uses Monadic actions? Should I always generalize my type signatures or just expect others to use the libraries when they need to? Also, to some extent it seems trivial to re-write a library like async with the generalized signatures I need. I would just need to apply 'lift' everywhere. Couldn't the compiler do this for me? ;-) Thanks, Dimitri