Recently I have a heated debate with my colleague about
a proper return type for async procedures
(procedure is just a function that returns
Unit in Scala.)
My point of view was that
Future[Unit] is the right type.
Let me present my way of reasoning here.
Let’s start with lifting.
Lifting in functional programming is defined as an operation that
transforms a function
f: A => B into a function
f': M[A] -> M[B],
M[_] is a functor. A functor
M[A], in simple words, is just a wrapper around a value of
A with addition of extra
map operation that can be used to transform the wrapped value.
A lot of types that we are using in our daily practice as programmers like
Future[T] are functors. Because Scala does not define
Functor[T] trait we cannot write a generic
lift function that could work with all these types. Instead we must write specific
lift functions that
work with only single functor type e.g.
We can use
liftOption to e.g. make
negate function work with
liftOption we could define
And then we can use it on a function that returns
Unit to get an async function that,
no surprise here, will return
But of course my colleague had his own arguments.
He pointed out that e.g.
submit method returns
Future<Void> as my
reasoning would suggest. So there must be a good reason why library designers
Future<?>, he continued, but when asked what exactly this reason could be
he was not able to answer. But he counter my question arguing that the standard
library establishes good patterns and practices, so if they use
then we should do the same in our code.
My colleague had a point. I needed to research the subject: why
Future<?> and not
After a bit of googling I noticed that
runAsync method of
does not follow the
Future<?> convention and instead returns
So it looks like the library designers changed their mind and now prefer returning
I pointed this out to my colleague, but he countered by saying that
is just uncomfortable to use. Consider this example he said:
Look at this ugly
.map(_ => ()) he said, it’s awful, isn’t it?
I pointed out that he can use for-comprehensions to get rid
of the last
map, but may argument was weak - the
fluent interface is often nicer and faster to work with.
Indeed I find it very strange that Scala does not provide a conversion from
Future[Unit] out of the box. In sync code we could write:
and the value returned by
deleteRecord would be automatically discarded.
Why can’t we have something similar in async code?
Yet this last problem pointed me to the following issue.
If we agree to use
Future[_] everywhere, then we may return by mistake
a value that should never be returned.
In other words we may inadvertently break object or module
There is also a small risk of a memory leak, if we return
say a huge array and the
Future[_] value will be kept for a while.
This is illustrated by the following example:
Seeing all these arguments, in the end, we decided to go with
it is not a perfect solution.
There is nothing better than a healthy debate BTW ;D