Haskell equivalent of C# 5 async/await
asynchronous, c#, haskell
Solution
Here's a `Task` monad that builds on top of the `async` library:
import Control.Concurrent.Async (async, wait)
newtype Task a = Task { fork :: IO (IO a) }
newTask :: IO a -> Task a
newTask io = Task $ do
w <- async io
return (wait w)
instance Monad Task where
return a = Task $ return (return a)
m >>= f = newTask $ do
aFut <- fork m
a <- aFut
bFut <- fork (f a)
bFut
Note that I haven't checked the monad laws for this, so it might not be correct.
This is how you would define primitive tasks that run in the background:
import Control.Concurrent (threadDelay)
test1 :: Task Int
test1 = newTask $ do
threadDelay 1000000 -- Wait 1 second
putStrLn "Hello,"
return 1
test2 :: Task Int
test2 = newTask $ do
threadDelay 1000000
putStrLn " world!"
return 2
Then you can combine `Task`s using `do` notation which creates a new deferred task ready to be run:
test3 :: Task Int
test3 = do
n1 <- test1
n2 <- test2
return (n1 + n2)
Running `fork test3` will spawn the `Task` and return a future which you can invoke at any time to demand the result, blocking if necessary until done.
To show that it works, I'll do two simple tests. First, I'll fork `test3` without demanding its future just to make sure it spawns the composite thread correctly:
main = do
fork test3
getLine -- wait without demanding the future
This works correctly:
$ ./task
Hello,
world!
<Enter>
$
Now we can test what happens when we demand the result:
main = do
fut <- fork test3
n <- fut -- block until 'test3' is done
print n
... which also works:
$ ./task
Hello,
world!
3
$
Problem
I just read about the new way to handle asynchronous functions in C# 5.0 using the `await` and `async` keywords. Examle from the C# reference on await: ``` private async Task SumPageSizesAsync() { // To use the HttpClient type in desktop apps, you must include a using directive and add a // reference for the System.Net.Http namespace. HttpClient client = new HttpClient(); // . . . Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); byte[] urlContents = await getContentsTask; // Equivalently, now that you see how it works, you can write the same thing in a single line. //byte[] urlContents = await client.GetByteArrayAsync(url); // . . . } ``` A `Task<byte[]>` represents the Future of an asynchronous task that will generate a value of type `byte[]`. Using the keyword `await` on a `Task` will basically put the rest of the function in a continuation which will be called when the task is done. Any function that uses `await` must use the keyword `async` and have type `Task<a>` if it would return type `a`. So the lines ``` byte[] urlContents = await getContentsTask; // Do something with urlContents ``` would translate into something like ``` Task newTask = getContentsTask.registerContinuation( byte[] urlContents => { // Do something with urlContents }); return newTask; ``` This feels a lot like a Monad (-transformer?). It feels like it should have some relation to the CPS monad, but maybe not. Here is my attempt at writing corresponding Haskell types ``` -- The monad that async functions should run in instance Monad Async -- The same as the the C# keyword await :: Async (Task a) -> Async a -- Returns the current Task, should wrap what corresponds to -- a async method in C#. asyncFunction :: Async a -> Async (Task a) -- Corresponds to the method Task.Run() taskRun :: a -> Task a ``` and a rough translation of the above example ``` instance MonadIO Async -- Needed for this example sumPageSizesAsync :: Async (Task ()) sumPageSizesAsync = asyncFunction $ do client <- liftIO newHttpClient -- client :: HttpClient -- ... getContentsTask <- getByteArrayAsync client url -- getContentsTask :: Task [byte] urlContents <- await getContentsTask -- urlContents :: [byte] -- ... ``` Would this be the corresponding types in Haskell? Is there any Haskell library this (or a similar way) implements way to handle asynchronous functions/actions? Also: Could you build this using the CPS-transformer? Edit Yes, the `Control.Concurrent.Async` module does solve a similar problem (and has a similar interface), but does so in an entirely different way. I guess that `Control.Monad.Task` would be a closer match. What (I think) I am looking for is a monadic interface for Futures that uses Continuation Passing Style behind the scenes.