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.
Here's a
Task
monad that builds on top of theasync
library: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:
Then you can combine
Task
s usingdo
notation which creates a new deferred task ready to be run:Running
fork test3
will spawn theTask
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:This works correctly:
Now we can test what happens when we demand the result:
... which also works:
The
monad-par
library providesspawn
andget
functios that can be used to createFuture
-like computations. You can either use thePar
monad for pure code that would be run in parallel, orParIO
for code with side-effects.In particular, I think that you're code example could be translated into:
As you can see,
spawn
is the responsible for creating code that runs in parallel, and returns anIVar
which can be later on queried byget
for retrieving its answer. I think the behavior of those two functions match very wellasync
andawait
.For more information I recommend you reading the
Par
monad chapter of Parallel and Concurrent Programming in Haskell.