4 ms·
I enjoyed reading this article, but does anyone who doesn't already understand what they're saying understand after reading something like: "Play's reactive co
by dmunoz 13y ago
I enjoyed reading this article, but does anyone who doesn't already understand what they're saying understand after reading something like:
"Play's reactive core and asynchronous libraries (e.g. WS) integrate seamlessly with other powerful concurrency primitives in the ecosystem such as Akka’s actors. Combining for-comprehensions with composable futures makes asynchronous concurrency look like straightforward synchronous code."
I get the gist, but I had to go and search for what for-comprehensions were. According to [0], they're a syntactic sugar over map. Then, if I search for composable futures (I know those words separately, but can only guess about them used together) I seem to only get results for Akka 2.0. The first result I click on is for a 167 page book for $23 USD. Maybe that is my fault, as the first result is actually to Akka documentation, but when I click there there is no mention of the word composable. The next two links are to slides of a presentation by the author of the first link I mentioned, and then to a video of the presentation. The fourth is to an early access of the same book, the next a blogspam to the video presentation. I gave up here.
For an article that is arguing for scala in comparison to PHP, Python, and Go, I didn't walk away with anything more than "sounds nice, but is it really?"
Code example would've helped.
[0] http://tataryn.net/2011/10/whats-in-a-scala-for-comprehension/ http://tataryn.net/2011/10/whats-in-a-scala-for-comprehensio...
Edit: Calling it "an article that is arguing for scala in comparison to PHP, Python, and Go" is a misrepresentation. It's really just about why they like scala, but they do do plenty of hand-wavy comparisons with other languages.
Edit 2: Okay, I see the Akka documentation does have a section "Composing Futures" that I missed.
- singingcheese 13y agoIt made perfect sense to me, perhaps because I code in Scala for a living (even though I don't use either Akka or Play (yet)). It reminds me of the old comment about how unreadable French is because it looks nothing like English.
- Sharlin 13y agoI don't think the target audience of the blog post was experienced Scala developers (that would just be preaching to the choir!) so the use of unexplained jargon should be kept at minimum to actually reach the audience.
- virtualwhys 13y agoval plusOne = for(x <- List(1,2,3)) yield x+1 desugars to: List(1,2,3).map(_+1) // or longhand, map(x=> x+1) and: for(x <- List(1,2,3); y <- List(4,5,6)) yield x*y desugars to: List(1,2,3).flatMap(x=> List(4,5,6).map(_*x)) So, composable futures means you can chain a bunch of futures together in a for comprehension and yield the successful or failing result without blocking. The essential point is that if you have some Thing that implements map and flatMap, it can be chained through a for comprehension. Only just dipping my toes in Haskell, but I must say that Scala is a truly wonderful language, one that makes my daily dev a whole lot of fun.
- deckiedan 13y agoWould this be equivalent to: plusOne = (x+1 for x in (1,2,3)) And then the second example: mult = (x*y for x,y in zip((1,2,3), (4,5,6))) In python?
- virtualwhys 13y agoyup, not sure what Python does with your examples under the hood, but they're equivalent, and, I must say, nicely concise ;-) In Scala I guess we could go zip-style and do: List(1,2,3) zip List(4,5,6) map{case(x,y)=> x*y} but I was trying to demo for comprehensions and their relation to map/flatMap for the OP.
- saryant 13y agoA for-comprehension from our production code: for { createResult <- Graph.createPerson(person) _ <- Search.putAll[Person](createResult \ "updated_nodes") _ <- Search.deletePersons(createResult \ "deleted_nodes") } yield { Created(createResult \ "node") } That code asynchronously updates both ElasticSearch and Neo4j. Those functions all return Scala futures containing JSON values. First the new object is created in Neo and then the index in ES is updated accordingly. This is in a controller action in a Play project where it's critical to keep everything non-blocking. If everything is successful, Play returns a 201 result to the requestor, otherwise there are error handling functions elsewhere that kick in.
- NickPollard 13y agoScala derail: You might have an unintentional happens-before relationship in that example: The deletePersons call will end up being in the flatMap of the putAll[Person] call, which means it wont be started until the putAll finishes, when by the looks of it you could do both at once (only the createPerson needs to happen first). You might want to do something like this: for { createResult <- Graph.createPerson(person) } yield { val a = Search.putAll[Person](createResult \ "updated_nodes") val b = Search.deletePersons(createResult \ "deleted_nodes") for ( _ <- a; _ <- b ) yield Created(createResult \ "node") } (notice starting the computations outside of the second for-comprehension, then just waiting for them inside)
- saryant 13y agoGood catch. :) Our production code actually does handle the relationship correctly, I just wanted to simplify in my example.
- dilap 13y agoWhat happens if the process dies in between createPerson and the search stuff? Are the Neo4j results kept around w/ the search index left stale, or is there some sort of wrapping transaction around it all?