2013-07-25 3 views
6

Ich erstelle eine Async-Bibliothek mit Scala 2.10-Futures. Der Konstruktor für die Bibliothek verwendet eine Folge von benutzerdefinierten Objekten, die ein bestimmtes Merkmal implementieren, und dann sendet eine Methode für die Bibliotheksklasse einige Daten einzeln in die benutzerdefinierten Objekte. Ich möchte, dass der Benutzer die ExecutionContext für die asynchronen Vorgänge bei der Einrichtung der Hauptinstanz bereitstellt und dann für diesen Kontext bei Bedarf in die benutzerdefinierten Objekte übergeben wird. Vereinfachte (? Pseudo-) Code:Impliziter ExecutionContext an enthaltene Objekte/Methoden übergeben

case class Response(thing: String) 

class LibraryObject(stack: Seq[Processor])(implicit context: ExecutionContext) { 
    def entryPoint(data: String): Future[Response] = { 
    val response = Future(Response("")) 
    stack.foldLeft(response) { (resp, proc) => proc.process(data, resp) } 
    } 
} 

trait Processor { 
    def process(data: String, resp: Future[Response]): Future[Response] 
} 

Es könnte wie folgt verwendet werden:

class ThingProcessor extends Processor { 
    override def process(data: String, response: Future[Response]) = { 
    response map { _.copy(thing = "THE THING") } 
    } 
} 

class PassThroughProcessor extends Processor { 
    override def process(request: Request, response: Future[Response]) = { 
    response 
    } 
} 

object TheApp extends App { 
    import ExecutionContext.Implicits.global 

    val stack = List(
    new ThingProcessor, 
    new PassThroughProcessor 
) 
    val libObj = new LibraryObject(stack) 

    val futureResponse = libObj.entryPoint("http://some/url") 

    // ... 
} 

ich einen Compiler-Fehler für ThingProcessor:

Kann nicht eine implizite ExecutionContext finden , benötigen Sie entweder selbst oder importieren Sie ExecutionContext.Implicits.global

Meine Frage ist, wie kann ich liefern implizit die ExecutionContext dass LibraryObject die benutzerdefinierten Objekte hat (ThingProcessor und PassThroughProcessor) oder die Methoden, ohne den Benutzer zu machen Sorgen darüber (die die Klassen schreiben werden) - das ist zu sagen, ich würde es vorziehen, dass der Benutzer hat nicht eintippen:

class MyFirstProcessor(implicit context: ExecutionContext) 

oder

override def process(...)(implicit context: ExecutionContext) = { ... } 

Antwort

5

der implizite Umfang Begleiter Objekte und Typparameter der Basis enthält Klassen.

Oder, library.submit (neue library.Processor {def process() ...}).

Dies funktioniert, aber war nicht mein erster Gedanke, die klüger sein sollte:

import concurrent._ 
import concurrent.duration._ 

class Library(implicit xc: ExecutionContext = ExecutionContext.global) { 
    trait Processor { 
    implicit val myxc: ExecutionContext = xc 
    def process(i: Future[Int]): Future[Int] 
    } 

    def submit(p: Processor) = p process future(7) 
} 

object Test extends App { 
    val library = new Library 
    val p = new library.Processor { 
    def process(i: Future[Int]) = for (x <- i) yield 2 * x 
    } 
    val res = library submit p 
    val z = Await result (res, 10.seconds) 
    Console println z 
} 

Update:

import concurrent._ 
import concurrent.duration._ 
import java.util.concurrent.Executors 

class Library()(implicit xc: ExecutionContext = ExecutionContext.global) { 
    trait Processor { 
    implicit val myxc: ExecutionContext = xc 
    def process(i: Future[Int]): Future[Int] 
    } 

    def submit(p: Processor) = p process future(7) 
} 

object ctx { 
    val xc = ExecutionContext fromExecutorService Executors.newSingleThreadExecutor 
} 
object library1 extends Library 
object library2 extends Library()(ctx.xc) 
object p extends library1.Processor { 
    def process(i: Future[Int]) = for (x <- i) yield 2 * x 
} 
object q extends library2.Processor { 
    def process(i: Future[Int]) = for (x <- i) yield 3 * x 
} 

object Test extends App { 
    val res = library1 submit p 
    //val oops = library2 submit p 
    //val oops = library1 submit q 
    val z = Await result (res, 10.seconds) 
    Console println z 
    Console println (Await result (library2 submit q, 10.seconds)) 
    ctx.xc.shutdownNow() 
} 

Es ist nicht weit hergeholt ist:

class Library(implicit xc: ExecutionContext = ExecutionContext.global) { 

    def submit(p: Processor): Future[Int] = p dueProcess future(7) 
} 
trait Processor { 
    implicit var myxc: ExecutionContext = _ 
    def dueProcess(i: Future[Int])(implicit xc: ExecutionContext) = { 
    myxc = xc 
    process(i) 
    } 
    protected def process(i: Future[Int]): Future[Int] 
} 

object ctx { 
    val xc = ExecutionContext fromExecutorService Executors.newSingleThreadExecutor 
} 
object Test extends App { 
    def db() = Console println (new Throwable().getStackTrace mkString ("TRACE [\n ", "\n ", "\n]")) 
    val library = new Library()(ctx.xc) 
    val p = new Processor { 
    protected def process(i: Future[Int]) = for (x <- i) yield { db(); 2 * x } 
    } 
    val res = library submit p 
    val z = Await result (res, 10.seconds) 
    Console println z 
    ctx.xc.shutdownNow() 
} 
+1

Dies ist nicht für Singleton-Objekte, klar ... können Sie sich einen Workaround vorstellen? –

+0

@ Pagoda_5b meinst du wie das Update? Entschuldigung im Voraus, wenn ich nicht klar denke. –

+0

Ja, sieht ziemlich glatt aus und ich mag es. –