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
)
If you want to use a
forcomprehension for this, I’d recommend looking at the Scala Language Specification for how for comprehensions are expanded tomap,flatMap, etc. This will give you some clues about how this structure relates to what you’ve already got (with nested calls toaddListener). You can then add an implicit conversion from the return type of thewaitForDatacall to a new type with the appropriatemap,flatMap, etc methods that delegate toaddListener.Update
I think you can use
scala.Responder[T]from the standard library:Assuming the class with the
addListeneris calledDispatcher[T]:You can then use this as requested
See the Scala wiki and this presentation on using
Responder[T]for a Comet chat application.