2010-04-24 7 views
9

Мне дана java api для подключения и связи через проприетарную шину с использованием стиля, основанного на обратном вызове. В настоящее время я внедряю приложение-доказательство в scala, и я пытаюсь понять, как я могу создать немного более идиоматический интерфейс scala.Могу ли я преобразовать этот асинхронный API-интерфейс Java в монадическое представление (или что-то еще идиоматическое)?

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

В Скале я, очевидно, могу определить неявное преобразование из (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 
    }) 
}) 

Глядя на это напомнило мне как scalaz обещаний и F # асинхронными рабочие процессы.

Мой вопрос заключается в следующем:

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

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

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

ответ

6

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

Update

Я думаю, что вы можете использовать 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 
)() 

См. the Scala wiki и this presentation при использовании Responder[T] для приложения чата Comet.

+1

Scalaz предоставляет «Monad [Responder]», поэтому у вас законно есть экземпляр монады, если вы делаете то, что предлагает здесь Блин Лингс. Кроме того, «Responder» - своего рода «универсальная монада», в которой вы можете реализовать любую другую монаду в терминах этого. – Apocalisp

+0

Ответчик дал мне точно правильное поведение, хотя по какой-то причине мне пришлось использовать 'def onEvent (t: T) = k (t)' вместо просто '= k' – AlecZorab

3

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

Мы должны будем ждать «реального» Scala-программиста, чтобы отразить эту идею. ;)