for {
user <- asyncGetUser(1)
company <- asyncGeCompany(user.companyid)
longresult <- asyncProcess(company.getSomething)
} yield longresult for {
user <- asyncGetUser(1)
company <- asyncGeCompany(user.companyid)
longresult <- asyncProcess(company.getSomething)
} yield longresultIt can be a problem. In Akka actors, referencing sender() from a future will be unpredictable because sender() could've changed in the mean time.
I think there are three strong solutions which address this problem:
1) Immutable state. Solves this problem completely but accidental capture remains an issue.
2) Hiding mutable state within actors. I hesitate to present this as a general solution though since it really requires going all-in with actors (I don't consider that to be a bad thing, necessarily).
3) Projects like Scala Spores [1] aim to tackle this at the compiler by capturing mutable references and executing async closures in an immutable environment and prohibiting accidental capturing. IOW, turning accidental capture into a compiler error. I'm excited about this one.
[1] https://speakerdeck.com/heathermiller/spores-distributable-f...
It's also somewhat unavoidable when you're dealing IO, unless you stick to .pipeTo(self)
user = getUser(1)
company = getCompany(user.companyId)
longresult = process(company.getSomething)
is a) simpler (because that's what your normal code looks like), b) performs exactly the same (as fibers basically do the same thing, only transparently), and c) retains context (like ThreadLocal variables).So if that was the only way, I'd say, fine. But once you have lightweight threads, they are always preferred (even Scala now has its own poor-man's lightweight threads in the form of async/await).
Actually, no. My code will always look like the for comprehension be it async or not, because my 'get' methods will always return an Either, Option or some other monadic context to represent the computation succeeding or not.
> performs exactly the same (as fibers basically do the same thing, only transparently),
Yes, I agree.
> c) retains context (like ThreadLocal variables).
Threadlocal variables are almost always code smell. If you find yourself using them, there is almost certainly a better way of accomplishing the same thing.
I don't see how having a Fork/Join threadpool that 'powers' the futures would be any slower than lightweight threads either.
With futures I can say, start operation 1 and operation 2 in parallel, then chain a callback to execute using both pieces of data, saving me some latency of doing the operations serially. How do you do this using quasar? Now that's a trivial example... what about arbitrary dependency trees? This falls out naturally with futures but I don't know of a nice way to do this with lightweight threads. e.g. 1 operation branches off into 5 other parallel operations which then chain some of their own additional callbacks for processing before finally bringing all the results back together, perhaps to form a json response. Does this example make sense?
Of course, you can keep using futures (what I call semi-blocking API), only futures that block the fiber rather than the thread, when you join them.
Of course you can have long-running fibers that interact with one another through channels. See: http://blog.paralleluniverse.co/2014/02/20/reactive/