Being a Nim newbie, I am trying to wrap my head around the new async/await feature, which I learnt is adapted from C#.
However goggling gives me some links which are not too promising about C# async/await.
http://mrange.wordpress.com/2014/05/29/why-i-wish-c-never-got-asyncawait/
http://ericlippert.com/2014/06/16/real-world-asyncawait-defects/
http://tomasp.net/blog/csharp-async-gotchas.aspx/
http://lunarfrog.com/blog/2012/01/23/simplicity-of-async-and-await/
http://www.jayway.com/2012/10/07/asyncawait-in-c-a-disaster-waiting-to-happen/
Isn't PFuture[T] good enough? it looks like a fine abstraction around IOCP / epoll / select.
In fact for me, understanding PFuture (a.k.a Promise in js?) and Windows IO Completion port is easier.
What are the benefits of having async/await on top of PFuture? And could Nim implementation possibly share C#'s issues?
PS: Thanks for creating Nim and its ecosystem. I am truly glad I discovered it. :)
It seems to me that the problems that the authors of these articles talk about are due to their inexperience with asynchronous programming. There are certainly gotchas and the Nim implementation has some too but it's all a case of learning how to use the feature properly in my opinion.
I also can't see what alternatives there are to async/await. The articles didn't seem to offer any.
If you want to use Future[T] on its own then by all means go ahead. It may in fact result in more efficient code. You will end up using a lot of callbacks though which have problems of their own.
Please do try async/await though as it is still very new and it needs testing. Let me know of any problems that you run into and I will be happy to help :)
@Araq: I see. It is reassuring to hear that.
@dom96: That is exactly the thing. In {.async.} land, things look a bit too easy and novices like me may unknowingly write stupid codes, and then it gets really hard to debug.
With callbacks, it's more complicated but it is nearer to metal, hence easier in a sense (to understand/debug).
I happen to be playing with asynchttpserver, and get compilation errors for the two snippets below, and ended up using PFuture as workaround.
import asyncdispatch, asynchttpserver, strtabs
# return a handler with a bound redirect path
proc redirect(url: string): proc(req: TRequest) {.async.} =
return proc (req: TRequest) {.async.} =
await respond(req, Http303, "", {"Location": url}.newStringTable())
asyncCheck newAsyncHttpServer().serve(TPort(80), redirect("/sub"))
runForever()
import asyncdispatch, asynchttpserver, strtabs
proc head*(req: TRequest, code: THttpCode, headers: varargs[tuple[key, val: string]]) {.async.} =
await respond(req, code, "", newStringTable(headers))
proc handler(req: TRequest) {.async.} =
await head(req, Http200, {"Content-type": "text/plain"})
asyncCheck newAsyncHttpServer().serve(TPort(80), handler)
runForever()
Like I said. Async/await is still very new and from what I can see you have in fact run into 3 different bugs.
In your first snippet the compiler says that "async is an invalid pragma". I guess macros cannot transform proc types? (Araq?). A workaround for this is to get rid of the {.async.} and add a Future[T] return type to that proc type, that is what the async macro would do if it got the opportunity to transform that proc type.
After doing so however I ran into a second bug. After the anonymous proc inside the redirect proc gets transformed by the async macro the compiler seems to get confused, it doesn't seem to notice that there is a return and complains that the proc needs to be discarded.
In your second snippet there seems to be a lambda variable capture bug. It does not want to capture the headers for some reason. A compiler bug it seems unfortunately.
Perhaps I am missing something though. I haven't investigated the cause of the issues very far.