Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I transform this asynchronous java network API into a monadic representation (or something else idiomatic)?

I've been given a java api for connecting to and communicating over a proprietary bus using a callback based style. I'm currently implementing a proof-of-concept application in scala, and I'm trying to work out how I might produce a slightly more idiomatic scala interface.

A typical (simplified) application might look something like this in Java:

    DataType type = new DataType();
    BusConnector con = new BusConnector();
    con.waitForData(type.getClass()).addListener(new IListener<DataType>() {
        public void onEvent(DataType t) {
            //some stuff happens in here, and then we need some more data
            con.waitForData(anotherType.getClass()).addListener(new IListener<anotherType>() {
                public void onEvent(anotherType t) {
                    //we do more stuff in here, and so on
                }
            });
        }
    });

    //now we've got the behaviours set up we call
    con.start();

In scala I can obviously define an implicit conversion from (T => Unit) into an IListener, which certainly makes things a bit simpler to read:

implicit def func2Ilistener[T](f: (T => Unit)) : IListener[T] = new IListener[T]{
  def onEvent(t:T) = f
}

val con = new BusConnector
con.waitForData(DataType.getClass).addListener( (d:DataType) => {
  //some stuff, then another wait for stuff
  con.waitForData(OtherType.getClass).addListener( (o:OtherType) => {
    //etc
  })
})

Looking at this reminded me of both scalaz promises and f# async workflows.

My question is this:

Can I convert this into either a for comprehension or something similarly idiomatic (I feel like this should map to actors reasonably well too)

Ideally I'd like to see something like:

for(
  d <- con.waitForData(DataType.getClass);
  val _ = doSomethingWith(d);
  o <- con.waitForData(OtherType.getClass)
  //etc
)
like image 886
AlecZorab Avatar asked Apr 24 '10 16:04

AlecZorab


People also ask

What is asynchronous API in Java?

A call which will not restrict a program from the execution of its code, and once the event is complete, the call returns back from the event to the CallBack function is known as an Asynchronous call.

What is the use of CompletableFuture in Java?

What is CompletableFuture? A CompltableFuture is used for asynchronous programming. Asynchronous programming means writing non-blocking code. It runs a task on a separate thread than the main application thread and notifies the main thread about its progress, completion or failure.

What is future object and how future is different from CompletableFuture?

As a result: Future transferes single value using synchronous interface. CompletableFuture transferes single value using both synchronous and asynchronous interfaces. Rx transferes multiple values using asynchronous interface with backpressure.

Is CompletableFuture a monad?

BTW, CompletableFuture is a monad too! The thenCompose method is analougous to the flatMap method of Stream and Optional . This also means that CompletableFuture satisfies the monad laws, one of which is that there is a method to wrap a value around with a CompletableFuture .


2 Answers

If you want to use a for comprehension for this, I'd recommend looking at the Scala Language Specification for how for comprehensions are expanded to map, flatMap, etc. This will give you some clues about how this structure relates to what you've already got (with nested calls to addListener). You can then add an implicit conversion from the return type of the waitForData call to a new type with the appropriate map, flatMap, etc methods that delegate to addListener.

Update

I think you can use scala.Responder[T] from the standard library:

Assuming the class with the addListener is called Dispatcher[T]:

trait Dispatcher[T] {
  def addListener(listener: IListener[T]): Unit
}

trait IListener[T] {
  def onEvent(t: T): Unit
} 

implicit def dispatcher2Responder[T](d: Dispatcher[T]):Responder[T] = new Responder[T} {
  def respond(k: T => Unit) = d.addListener(new IListener[T] {
    def onEvent(t:T) = k
  })
}

You can then use this as requested

for(
  d <- con.waitForData(DataType.getClass);
  val _ = doSomethingWith(d);
  o <- con.waitForData(OtherType.getClass)
  //etc
) ()

See the Scala wiki and this presentation on using Responder[T] for a Comet chat application.

like image 88
Ben Lings Avatar answered Oct 06 '22 20:10

Ben Lings


I have very little Scala experience, but if I were implementing something like this I'd look to leverage the actor mechanism rather than using callback listener classes. Actors were made for asynchronous communication, they nicely separate those different parts of your app for you. You can also have them send messages to multiple listeners.

We'll have to wait for a "real" Scala programmer to flesh this idea out, though. ;)

like image 29
Carl Smotricz Avatar answered Oct 06 '22 21:10

Carl Smotricz