Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit ExecutionContext priority in Scala 2.12

In Scala 2.12 importing the global execution context and then having another implicit execution context defined in the scope results in an ambiguous implicit, while in 2.11 it works just fine.

import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global

class A(implicit ec: ExecutionContext) {
  x = implicitly[ExecutionContext]
}

Compiler gives error:

error: ambiguous implicit values:
 both lazy value global in object Implicits of type => scala.concurrent.ExecutionContext
 and value ec in class A of type scala.concurrent.ExecutionContext
 match expected type scala.concurrent.ExecutionContext
       val x = implicitly[ExecutionContext]
                         ^

What is the cause of this and how to work around it in code?

like image 346
ochrons Avatar asked Nov 05 '16 20:11

ochrons


1 Answers

The spec treats overload resolution as the disambiguation of a selection of members of a class. But implicit resolution uses static overload resolution to choose between references which are not members.

Arguably, the following is a misinterpretation of the spec, since zzz is defined in a class derived from X much as yyy is:

$ scala
Welcome to Scala 2.12.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_101).
Type in expressions for evaluation. Or try :help.

scala> import concurrent._, ExecutionContext.global
import concurrent._
import ExecutionContext.global

scala> trait X { implicit val xxx: ExecutionContext = global }
defined trait X

scala> class Y extends X { implicit val yyy: ExecutionContext = global ; def f = implicitly[ExecutionContext] }
defined class Y

scala> class Z extends X { def f(implicit zzz: ExecutionContext) = implicitly[ExecutionContext] }
<console>:16: error: ambiguous implicit values:
 both value xxx in trait X of type => scala.concurrent.ExecutionContext
 and value zzz of type scala.concurrent.ExecutionContext
 match expected type scala.concurrent.ExecutionContext
       class Z extends X { def f(implicit zzz: ExecutionContext) = implicitly[ExecutionContext] }
                                                                             ^

Currently, you must rely on naming to shadow the implicit from enclosing scope:

scala> class Z extends X { def f(implicit xxx: ExecutionContext) = implicitly[ExecutionContext] }
defined class Z

Or,

scala> :pa
// Entering paste mode (ctrl-D to finish)

package object p { import concurrent._ ; implicit val xxx: ExecutionContext = ExecutionContext.global }
package p { import concurrent._ ;
  class P { def f(implicit xxx: ExecutionContext) = implicitly[ExecutionContext]
            def g = implicitly[ExecutionContext] }
}

// Exiting paste mode, now interpreting.


scala> 
like image 70
som-snytt Avatar answered Oct 05 '22 04:10

som-snytt