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