Passing implicit ExecutionContext to contained objects/called methods
The implicit scope includes companion objects and type parameters of base classes.
Or, library.submit(new library.Processor { def process() ... }).
This works, but wasn't my first thought, which was to be more clever:
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()
}
It isn't much of a stretch to:
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()
}
Michelle Tilley
I'm a software developer living in the San Francisco Bay Area. I specialize in web programming and scripting, especially Node.js, Svelte, React, and Flux/Redux, though I am familiar with several languages and frameworks. Previously I worked a lot with Rails and Angular.js. I absolutely love to learn new languages and technologies—recent favorites include Elm, Elixir, and Rust. I work as a Software Engineer at GitHub where I work on Primer, GitHub’s design system. I’ve also worked on Electron, and Atom, focusing on the Git and GitHub integration, and a few other projects. BinaryMuse @ GitHub My Blog NodeCasts, Free Node.js Screencasts BinaryMuse @ Twitter My Stack Overflow CV If you wish to contact me, you can do so via any of these methods.
Updated on June 05, 2022Comments
-
Michelle Tilley almost 2 years
I'm creating an async library using Scala 2.10 futures. The constructor for the library takes a sequence of user-defined objects that implement a certain trait, and then a method on the library class sends some data one-by-one into the user-defined objects. I want the user to provide the
ExecutionContext
for the async operations when setting up the main instance, and then for that context to get passed into the user-defined objects as necessary. Simplified (pseudo?)code: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] }
It might be used something like this:
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") // ... }
I get a compile error for
ThingProcessor
:Cannot find an implicit ExecutionContext, either require one yourself or import
ExecutionContext.Implicits.global
My question is, how do I implicitly supply the
ExecutionContext
thatLibraryObject
has to the user-defined objects (ThingProcessor
andPassThroughProcessor
) or their methods without making the user (who will be writing the classes) worry about it--that is to say, I would prefer that the user did not have to type:class MyFirstProcessor(implicit context: ExecutionContext)
or
override def process(...)(implicit context: ExecutionContext) = { ... }