Transforming java Listeners into Scala Futures
When using Java libraries from Scala, if the library provides some
sort of async interface you often need to instantiate a Java class
that acts as listener. This is a bit cumbersome when working in Scala,
you would expect the external library method call to return a
Future[T] so you can use idiomatic monadic operations in your Scala
code.
One pattern I use often to get around this limitation is to use
Promise[T], a "future factory", to transform a Java listener into
something that provides a Future[T] we can use.
For example, when using the twitter4j library and it's async API, you could be using a listener instance like this:
class TwitterClientUpdateListener(callbackObj: T) extends TwitterAdapter {
override def updatedStatus(status: Status): Unit = {
// do something with `status`, probably interacting with callbackObj
}
override def onException(te: TwitterException, method: TwitterMethod): Unit = {
// do something with `te`, probably interacting with callbackObj
}
}
And you also need to instantiate this class:
def tweet(text: String): String = {
val twitter = twitterFactory.getInstance
twitter.addListener(new TwitterClientUpdateListener(callbackObj))
twitter.updateStatus(text)
// Mess around with callbackObj so we can get the side effects applied so we can return String
}
Wouldn't it be nice if tweet returned a Future[String] instead? We
could then embrace the async nature of the API we are using and
whatever is calling tweet could operate on a Future[String]
instead. We can easily use the a Promise[String] in this case:
class TwitterClientUpdateListener(val promise: Promise[String]) extends TwitterAdapter {
override def updatedStatus(status: Status): Unit = {
promise.success(status.getText)
}
override def onException(te: TwitterException, method: TwitterMethod): Unit = {
promise.failure(te)
}
}
def tweet(text: String): Future[String] = {
val p = Promise[String]()
val twitter = twitterFactory.getInstance
twitter.addListener(new TwitterClientUpdateListener(p))
twitter.updateStatus(text)
p.future
}
tweet now returns a standard Future[String].
I see this problem when using many Java async API and I often use this small pattern to keep my Scala code more idiomatic.
comments powered by Disqus