2010-04-24 21 views
9

J'ai reçu une API Java pour la connexion et la communication via un bus propriétaire en utilisant un style de rappel. Je suis en train de mettre en place une application de preuve de concept en scala, et j'essaye de trouver comment je pourrais produire une interface de scala légèrement plus idiomatique.Puis-je transformer cette API de réseau java asynchrone en une représentation monadique (ou quelque chose d'autre idiomatique)?

Une application typique (simplifiée) pourrait ressembler à ceci en 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(); 

En scala je peux définir évidemment une conversion implicite de (T => Unité) dans un IListener, ce qui rend certainement choses peu plus simple à lire:

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

en regardant cela me rappelle des deux promesses scalaz et f workflows # async.

Ma question est la suivante:

Puis-je convertir en soit une pour la compréhension ou quelque chose de la même idiomatiques (je me sens comme cela devrait cartographier les acteurs assez bien aussi)

Idéalement, je voudrais voir quelque chose comme:

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

Répondre

6

Si vous voulez utiliser une compréhension for pour cela, je vous conseille de regarder la Scala spécification du langage pour faire pour compréhensions sont développés à map, flatMap, etc. Cela vous donnera quelques indices sur la façon dont cette structure se rapporte à ce que vous avez déjà (avec des appels imbriqués à addListener). Vous pouvez ensuite ajouter une conversion implicite à partir du type de retour de l'appel waitForData à un nouveau type avec les méthodes appropriées map, flatMap, etc qui délèguent à addListener.

Mise à jour

Je pense que vous pouvez utiliser scala.Responder[T] de la bibliothèque standard:

En supposant que la classe avec le addListener est appelé 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 
    }) 
} 

Vous pouvez alors l'utiliser comme demandé

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

Voir the Scala wiki et this presentation sur l'utilisation de Responder[T] pour une application de conversation Comet.

+1

Scalaz fournit 'Monad [Responder]', donc vous avez légitimement une instance monad si vous faites ce que Ben Lings suggère ici. En outre, 'Responder' est une sorte de" monade universelle "en ce sens que vous pouvez implémenter n'importe quelle autre monade. – Apocalisp

+0

Répondeur m'a donné exactement le bon comportement, bien que pour une raison quelconque, j'ai dû utiliser 'def onEvent (t: T) = k (t)' au lieu de simplement '= k' – AlecZorab

3

J'ai très peu d'expérience Scala, mais si j'exécutaient quelque chose que je regarderait comme cela pour tirer parti du mécanisme d'acteur plutôt que d'utiliser des classes d'écoute de rappel. Les acteurs ont été créés pour la communication asynchrone, ils séparent joliment ces différentes parties de votre application pour vous. Vous pouvez également les faire envoyer des messages à plusieurs auditeurs.

Nous allons devoir attendre qu'un "vrai" programmeur Scala élabore cette idée. ;)