Могу ли я преобразовать этот асинхронный сетевой API Java в монадическое представление (или что-то еще идиоматическое)? - PullRequest
9 голосов
/ 24 апреля 2010

Мне дали Java API для подключения и связи через проприетарную шину в стиле обратного вызова. В настоящее время я реализую приложение для проверки концепции в Scala и пытаюсь понять, как создать немного более идиоматический интерфейс Scala.

Типичное (упрощенное) приложение может выглядеть примерно так в 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();

В scala я, очевидно, могу определить неявное преобразование из (T => Unit) в IListener, что, безусловно, делает вещи немного проще для чтения:

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
  })
})

Это напомнило мне как обещания скалаза, так и рабочие процессы f # async.

Мой вопрос такой:

Могу ли я преобразовать это либо в «для понимания», либо во что-то подобное идиоматическое (я чувствую, что это также должно довольно неплохо отображаться на актеров)

В идеале я бы хотел увидеть что-то вроде:

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

Ответы [ 2 ]

6 голосов
/ 24 апреля 2010

Если вы хотите использовать для этого понимание for, я бы порекомендовал взглянуть на Спецификацию языка Scala, чтобы узнать, как расшифровки понимаются до map, flatMap и т. Д. как эта структура связана с тем, что у вас уже есть (с вложенными вызовами addListener). Затем вы можете добавить неявное преобразование из возвращаемого типа вызова waitForData в новый тип с соответствующими методами map, flatMap и т. Д., Которые делегируют addListener.

Обновление

Я думаю, вы можете использовать scala.Responder[T] из стандартной библиотеки:

Предполагается, что класс с addListener называется 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
  })
}

Вы можете использовать это по запросу

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

См. вики Scala и этой презентации об использовании Responder[T] для приложения чата Comet.

3 голосов
/ 24 апреля 2010

У меня очень мало опыта в Scala, но если бы я реализовывал нечто подобное, я бы попытался использовать механизм акторов, а не использовать классы прослушивателей обратного вызова. Актеры были созданы для асинхронного общения, они прекрасно разделяют эти разные части вашего приложения для вас. Вы также можете сделать так, чтобы они отправляли сообщения нескольким слушателям.

Нам придется подождать, пока «настоящий» программист Scala осуществит эту идею. ;)

...