Alexandr
Alexandr

Reputation: 9515

Scala: Invokation of methods with/without () with overridable implicits

Here is a definition of method, that uses ExecutionContext implicitly, and allows client to override it. Two execution contexts are used to test it:

val defaultEc = ExecutionContext.fromExecutor(
    Executors.newFixedThreadPool(5))

Names of threads look like: 'pool-1-thread-1' to 'pool-1-thread-5'

And the 2nd one from Scala:

scala.concurrent.ExecutionContext.Implicits.global

Names of threads look like: 'scala-execution-context-global-11'

Client can override default implicit via:

implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global

Unfortunately it is overridable only, when a method with implicit is invoked without ():

val r = FutureClient.f("testDefault") //prints scala-execution-context-global-11

not working:

val r = FutureClient.f("testDefault")() //still prints: pool-1-thread-1

The question is WHY it works this way? Cause it makes it much more complicated for clients of API

Here is a full code to run it and play:

object FutureClient {
  //thread names will be from 'pool-1-thread-1' to 'pool-1-thread-5'
  val defaultEc = ExecutionContext.fromExecutor(
    Executors.newFixedThreadPool(5))

  def f(beans: String)
           (implicit executor:ExecutionContext = defaultEc)
  : Future[String] = Future {
    println("thread: " + Thread.currentThread().getName)
    TimeUnit.SECONDS.sleep(Random.nextInt(3))
    s"$beans"
  }
}

class FutureTest {
  //prints thread: pool-1-thread-1
  @Test def testFDefault(): Unit ={
    val r = FutureClient.f("testDefault")
    while (!r.isCompleted) {
      TimeUnit.SECONDS.sleep(2)
    }
  }

  //thread: scala-execution-context-global-11
  @Test def testFOverridable(): Unit ={
    implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global
    val r = FutureClient.f("testDefault")
    while (!r.isCompleted) {
      TimeUnit.SECONDS.sleep(2)
    }
  }

  //prints pool-1-thread-1, but not 'scala-execution-context-global-11'
  //cause the client invokes f with () at the end
  @Test def testFOverridableWrong(): Unit ={
    implicit val newEc = scala.concurrent.ExecutionContext.Implicits.global
    val r = FutureClient.f("testDefault")()
    while (!r.isCompleted) {
      TimeUnit.SECONDS.sleep(2)
    }
  }
}

I have already discussed a couple of related topics, but they are related to API definition, so it is a new issue, not covered by those topics.

Upvotes: 0

Views: 76

Answers (1)

Dmytro Mitin
Dmytro Mitin

Reputation: 51723

Scala Patterns To Avoid: Implicit Arguments With Default Values

f("testDefault") (or f("testDefault")(implicitly)) means that implicit argument is taken from implicit context.

f("testDefault")(newEc) means that you specify implicit argument explicitly. If you write f("testDefault")() this means that you specify implicit argument explicitly but since the value isn't provided it should be taken from default value.

Upvotes: 5

Related Questions