Passing implicit ExecutionContext to contained objects/called methods

11,195

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()
}
Share:
11,195
Michelle Tilley
Author by

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, 2022

Comments

  • Michelle Tilley
    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 that LibraryObject has to the user-defined objects (ThingProcessor and PassThroughProcessor) 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) = { ... }