Skip to content

Comment on Scaling Scala vs Java

Comments

This blog post is not great. It's also not about scaling at all.

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.

https://www.refheap.com/20640

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.

I don't understand much of what you've written. Perhaps you can elaborate? Specifically:

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.

a promises based async implementation is not incredibly helpful when you have a strictly one-after-another ordering of data dependencies

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.

Monads are used in Clojure, just not for this particular use-case because it's unnecessary the majority of the time.

A good example of monad (and macros) use would be: http://github.com/clojure/core.async

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.