After some time working in Scala some common problems look easier from a functional programming perspective, especially when working in parallel systems.
I had to write some multithreading code today, in Groovy. I am not a Groovy expert, but far from it. However, writing concurrent code should not be that hard no matter the language, right?
The problem in question is the following. I need to run some code, A and when A finishes I need to run some other code B and C. These chains could be as complicated as I need them to be.
I looked at Groovy concurrent toolset. Well, I can create Futures. Nice! This should be easy enough.
Thinking about Future in Scala I wanted to do.
This is a very good way to chain async computations.
Now, if I have another chain of computations, let’s say anotherFuture
We can sync them using Scala for.
In here we can use for because Future is a Monad, or kind of, at least it behaves like one most of the time.
Going back to my Groovy code just to discovered that Futures there have little in common to the Scala ones.
In Groovy, to run an async operation, we need something like this.
In order to get the result from future
we have to do future.get()
In here .get()
will block in the same way Scala does. The problem is that there is no way I could find so that I can define what to do with the result of future
without blocking.
Of course, I could always submit more than one task to the pool by doing.
But this is NOT what I need. I have to be able to chain operations and pass the result of a task to the next async task.
Well, I ended implementing it myself.
We will call my new Future Continuation
, this is just a name I picked, nothing specific. It will inherit from Future so we could also send it to an execution pool and we will finally add a monadic map function or method which is how they are called in Groovy.
In order to create a Continuation, we need to pass the Future it will use.
Continuation implements the same function that Future but using the underlying future.
The most important part is that Continuation adds a new function, map
.The function map
receives a Closure<B>
and ExecutorService
which is the execution pool and returns a new Continuation<B>
by creating a new Future that gets the value from async and passed to the Closure
. Notice that this is a non-blocking operation.
Going back to the original problem.
We could now do in Groovy.
In here, y
is the result of someLogicB(x)
that gets passed to next .map
.
Notice that we are passing pool
to each map, but remember that Scala Futures need an ExecutionContext that is passed implicitly.
Scala concurrency rocks, that is not a secret. However, we cannot be locked out by the language of our choice (Scala is mine). Sometimes we need to solve a problem with the tools we have on hand. If you don’t have or find the required tool, then go and build it yourself.
Don’t fear the Monad
. Monads are mathematical structures with vast of uses in programming, especially in functional programming. Monads are a very interesting ways of chaining operations, as we could see with map
. Our Continuation
is not a full Monad, but the idea behind map
is coming from there.
Remember to write your tests first.