Scaling Scala vs Java
jazzy.id.au
jazzy.id.au
He is not comparing Scala vs Java. He's comparing Play Framework (async i/o) vs. a Servlet that doesn't use async i/o.
He admits later that he purposefully handicapped the Java version. His reason is, he claims that Java's Promise pattern is so hard to program that "Java developers don't do that." Which.. wow. His evidence: he's written a lot of Java apps and he's personally never used it.
And so he just switches off async i/o for Java.
We all know Java has more boilerplate. The Promise pattern is 2 lines of boilerplate code per call, much like an event handler. To go from that to claiming "Java developers don't do" Promise is a huge, unsupported leap.
Also you'd get the impression from his writing that Play Framework is only available for Scala.. but there's a Java API too.
The "Promise pattern" is extremely awful for Java. Yes, Play does allow Java developers to do Async I/O by means of Futures/Promises. That doesn't make it any less awful and a quick search on Play's mailing list should highlight it. The reason for it is because working with Future/Promise instances involves a lot of calls to map() and flatMap(). In comparison to java.util.concurrent.Future (which sucks), these Futures are non-blocking and can be processed and transformed without doing a .get() ... We aren't talking about one or two calls, but rather about chains of calls. Scala is a language designed for this, it even has monad comprehensions and an upcoming library that should bring C#'s async [1]
Iteratees are also one of the coolest notions for dealing with streams of data that ever came out of the FP world. And if in Java you can sort of limp around when it comes to simple Future/Promise objects, well Iteratees are in a different league [2] [3]
The Play developers did an extremely awesome job by exposing a clean Java API. But some things are sorely missing, as there's no way to expose them in Java cleanly, which is why the internals of Play's server are written in Scala.
Also, it does make sense to compare frameworks in language comparisons. For example you can't compare C# versus Java for web development, without comparing the frameworks themselves. If Play also allows you to use Java, that's only testament to the awesomeness of Scala, of the JVM and of Play's developers.
[1] https://github.com/scala/async
[2] http://mandubian.com/2012/08/27/understanding-play2-iteratee...
[3] http://www.playframework.com/documentation/2.2.x/Iteratees
Sure, Play has some nice concepts - but the fact that it's built in Scala (and that there is a Java API) means that all of the things that it makes possible are also possible in plain old Java! So this isn't a performance comparison by any means, but rather a "look at what you can do in similar amounts of code" comparison, which is a pretty shallow comparison.
Check out the author's previous post where he concludes that Scala is faster than Java (whatever that means, the whole concept seems to ignore the fact that Scala compiles to JVM bytecode) was 18% faster (on a single execution? average execution? author doesn't say): https://jazzy.id.au/default/2012/10/16/benchmarking_scala_ag...
If you follow that argument, what you can do in Java that you can't do by writing bytecode by hand? Or, what you can do with a JVM lang that you can't do in assembler? His argument is that even you can do the same in Java, is so unnatural that (almost) nobody does it.
You haven't made your Java webapps robust and tolerant of failure because you don't like the extra 3 lines of code you have to write? That makes me question you as a developer and the value you place on things, not the language/ecosystem.
Quite the contrary. I (the author) have done a lot of asynchronous Java development using promises and other asynchronous constructs. The boilerplate is a cost that I'm willing to accept.
However, in my experience, working with other Java developers as well as consulting and providing support to quite a number of enterprise companies full of Java developers, I find time and time again that it is not a cost that other Java developers are willing to accept. You provide them with fluent async libraries, you educate them again and again why they shouldn't block on calls to remote clients, you provide sample code, and they ignore your advice because the code is too hard to read. I'm not saying that all Java developers are like this, I'm just saying that from my observations working across many enterprise companies, most are like this.
And do you think those developers are likely to adopt Scala?
Java is one of the most popular languages and so statistically you're going to find a lot more poor talent out there. If they can't handle 2 lines of boilerplate I shudder at the horrors those same folks would unleash with Scala.
For one thing, a promises based async implementation is not incredibly helpful when you have a strictly one-after-another ordering of data dependencies.
In the interest of comparison, however, I decided to translate the example code to the equivalent synchronous and asynchronous Clojure code.
Things to note:
Instead of being forced to use a compiler-assisted construct, promises on Clojure get dereferenced. Dereferencing is the same operation used to get the value of agents, atoms, and refs as well.
Deref'ing (@) a promise over and over is totally okay.
Using promises doesn't make the Scala solution meaningfully or usefully asynchronous unless the handler is yielding its thread while blocking on I/O.
> It's also not about scaling at all.
It's about handling many simultaneous connections. How is this not a specific type of scaling, and one that is relevant to many web sites?
> a promises based async implementation is not incredibly helpful when you have a strictly one-after-another ordering of data dependencies.
What does this mean beyond the fact that data dependencies limit parallelism?
> Instead of being forced to use a compiler-assisted construct
For comprehensions in Scala are syntax for the flatMap and map functions. You're not forced to use them, and for comprehensions work with any monad.
> Deref'ing (@) a promise over and over is totally okay.
Mapping a promise (the Scala equivalent) over and over is totally ok.
> Using promises doesn't make the Scala solution meaningfully or usefully asynchronous unless the handler is yielding its thread while blocking on I/O.
What does the IO model of the JVM have to do with Scala futures? Blocking IO operations are blocking, whatever language you use. There are three ways around this:
1. Use nonblocking IO
2. Use bytecode manipulation to transform blocking to nonblocking IO
3. Use a separate thread pool for blocking IO operations.
1 and 3 are the typical solutions in Scala.
I don't understand what you mean. Of course it's helpful.
memcached.get[String]("someKey") flatMap {
case Some(value) => Future.successful(value)
case None =>
database.query(...).flatMap { value =>
memcached.set("someKey", value, 10.minutes)
}
}
The above fetches a key from memcached and in case it doesn't exist, it fetches it from the database and once that's done, it sets the value in memcached and once that's done the processing is done and thus you can continue with other things. If the clients are non-blocking, than that's totally asynchronous and scalable.In Clojure you don't see this because in Clojure people don't work with Monads.
> Using promises doesn't make the Scala solution meaningfully or usefully asynchronous unless the handler is yielding its thread while blocking on I/O.
The idea of Futures is not to use blocking I/O, but rather to either:
1) use non-blocking I/O, or
2) delegate the blocking I/O call to some sort of actor, then continue the execution once that's done
Both are possible and quite easy.
A good example of monad (and macros) use would be: http://github.com/clojure/core.async
It's still a bit to early for me to migrate from Akka. But thanks for the great work you are doing!
But although I have a production running Scala project, I cringe every time I have to work with Scala just because of the tooling. It's sad to see that SBT is cryptic and IDEs are slow and misleading.
When I work on the JVM I prefer Clojure. I think lein makes an exemplary development, build, and dependency management tool. I wish Scala had that.
I suspect the awfulness of SBT is causing a lot of harm to Scala adoption.
http://blog.jetbrains.com/scala/2013/07/17/sbt-plugin-nightl...
Gradle did come up and I was astonished to discover Groovy is now over its performance problems and may very well be a worthy platform to develop web services on!. Only if Groovy had a clear road map and profound backing it would have been the true "Scala killer".
I think it's worth giving Groovy a second chance by its own, and I've set up some time to do that.
And lastly - after looking at gradle as a build tool for Scala - it looks OK, however I don't think I will ever withstand all of the hate and incompatibility from the Scala community because I've deviated from SBT (which is another problem by itself).
I do develop a lot with Groovy these days, love it!
It isn't. Gradle still ships with the slower Groovy 1.x, and Grails, altho it ships with Groovy 2.x, doesn't actually use any of the faster static-typing, presumably because it's still too buggy.
It would be nice if Gradle used Scala or Kotlin as its build language instead, but with a name like "GradleWare", I suspect someone from VMWare is pulling the strings to keep Gradle tied to Grails (and Groovy).
func stockForProdsTO(uid int, timeout time.Duration) (*Stock, error) {
stockCh := make(chan *Stock, 1)
go func() {
orders := ordersForUser(userById(uid).email)
stockCh <- stockForProds(prodsForOrders(orders))
}()
select {
case result := <-stockCh:
return result, nil
case <-time.After(timeout):
return nil, errors.New("Could not get stock for product before timeout.")
}
}