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