# Composing IO and Async in haskell

Whether based on MVar or TVar, async implementation are always based on operation on some underlying monad IO and STM.

Making Async a monad on its own, as in F# async computation builder, if done in a naïve way require littering with unsafePerformIO which does not feel very haskellish.

module Async2(Async, async, wait) where

import           Control.Concurrent (forkIO)
import Control.Concurrent.MVar
import System.IO.Unsafe

data Async a = Async (MVar a)

wait :: Async a -> IO a
wait  (Async m) = takeMVar m

-- we can't lift it without unsafeperformIO !
async :: IO a -> Async a
async action = unsafePerformIO $do m <- newEmptyMVar forkIO$ do r <- action; putMVar m r
return $Async m -- we can't make it a monad without unsafePerformIO ! instance Monad Async where return a = Async$ unsafePerformIO $newMVar a m >>= f = let a = unsafePerformIO$ wait m
in f a


I can forego having a monad and rely on nested function composition, but that's not very nice compared to do notation. So instead I can try to add some MonadIO somewhere:

module Async3(Async, async, wait) where

import           Control.Concurrent.MVar
import           Control.Concurrent (forkIO)

data Async m a =  Async (m (MVar a))

async ::  MonadIO m =>  IO a -> (Async m a)
async action = Async $liftIO$ do
m <- newEmptyMVar
forkIO $do r <- action; putMVar m r return m wait :: MonadIO m => Async m a -> m a wait (Async m) = do mv <- m liftIO$ readMVar mv

return a = Async $liftIO$ newMVar a
ma >>= f = Async $do r <- wait ma let (Async mv) = f r mv -- automatic def instance MonadIO m => Functor (Async m) where fmap f a' = a' >>= pure . f instance MonadIO m => Applicative (Async m) where pure = return (<*>) = ap instance MonadIO m => MonadIO (Async m) where liftIO m = Async ( liftIO undefined)  When parametarized by such MonadIO, I can leverage my IO context to get the monadic do notation. #!/usr/bin/env stack -- stack --install-ghc --resolver lts-5.13 runghc --package http-conduit module MainAsync3 where import Control.Concurrent(forkIO, threadDelay) import Async3(Async, async, wait) main :: IO () main = do c <- wait$ do
r <- do

async $threadDelay 1000000 return "hello" s <- do async$ threadDelay 1000000
return " world"
return $r ++ s print c return ()  Except I can't perform IO from within Async m itself, so that motivates another round: module Async4(Async, async, wait) where import Control.Concurrent.MVar import Control.Concurrent (forkIO) import Control.Monad import Control.Monad.IO.Class data Async m a = Async (m (Res a)) data Res a = RMVar (MVar a) | Done a async :: MonadIO m => IO a -> Async m a async action = Async$ liftIO $do m <- newEmptyMVar forkIO$ do r <- action; putMVar m r
return $RMVar m wait :: MonadIO m => Async m a -> m a wait (Async m) = do r <- m case r of RMVar mv -> liftIO$ readMVar mv
Done a -> return a

return a = Async $return$ Done a
ma >>= f = Async $do r <- wait ma let (Async mv) = f r mv -- automatic def instance MonadIO m => Functor (Async m) where fmap f a' = a' >>= pure . f instance MonadIO m => Applicative (Async m) where pure = return (<*>) = ap instance MonadIO m => MonadIO (Async m) where liftIO m = Async ( liftIO$ Done <$> m)  Which we can call using: #!/usr/bin/env stack -- stack --install-ghc --resolver lts-5.13 runghc module MainAsync4 where import Control.Concurrent(forkIO, threadDelay) import Async4(Async, async, wait) import Control.Monad.IO.Class main :: IO () main = do putStrLn "starting" c <- wait$ do  -- I can wait an async computation
r <- do -- I can compose sequentially async
liftIO $putStrLn "computing hello" -- I can do IO within async async$ threadDelay 1000000
return "hello"
s <- do
async $threadDelay 1000000 return " world" return$ r ++ s

print c
return ()


I feel like it is quite complicated, though. What approaches are there to regain some sanity and nicely hide those calls so that I can get a monad modulo some IO and write nice imperative looking code while composing async operations?

In the end, I know that IO might launch missiles but when you don't launch missiles there must be ways to declare it somehow.

Are there ways known to be superior to deal with these situations? The same problem arises if we swap IO with STM. It's just that I build a new composable language on top of another.

Would effect handlers provide a better answer for instance?

Async3

I'm not sure your MainAsync3 works the way you want it to. When I bump the threadDelays values to 5 seconds each the running time increases to 10 seconds suggesting that the async operations are not being done in parallel. And also the same for MainAsync4. Am I missing something?

Btw, a simple way to see wall clock time is to:

import Data.Time
...

main = do
getCurrentTime >>= print
...
getCurrentTime >>= print


Note that timing functions like timeIt returns execution time which will be near 0 for these examples.

Control.Concurrent.Async

Have a look at the async package. It implements async within the IO monad. AFAICT it does not use unsafePerformIO.

If you can perform async operations in IO, why should we create a special monad for it? Why does F# have an Async monad?

Here are some reasons I came up with:

1. The bind operation (let! in F#?) will automatically wait on computations. On the other hand, normal IO operations have to be lifted.

2. In Haskell if it's a monad it is also a Functor and Applicative, and then we can use generic applicative operations like fmap, (<*>), etc. That's the point of the promise package - it just wraps the result of an async in a newtype to give you a monad to use.

3. One primary reason for implementing a computation as a monad is to restrict what it can do. For instance, the ST monad is designed so that the thread state can't escape the computation.

4. Another reason to implement a monad is allow you to implement a "run" function which controls execution of the computation. For instance, Haxl implements an async monad which can cache results and batch together multiple requests sent to the same service.

5. All I know about the F# Async monad I learned from this blog post. Perhaps you could give more insight as to what's useful about the F# Async monad.