Search code examples
scalaimplicit

Could not find implicit value inside singleton object


I have this code:

trait Context {
  implicit val e: Encoder

  trait Encoder {
    def write(): Unit = {
      println("Test")
    }
  }

}

trait AsyncEncoders {
  this: Context =>

  class AsyncEncoder extends Encoder {
  }

  implicit val e = new AsyncEncoder()
}

class ConcreteContext extends Context with AsyncEncoders {
}

When I use it like this (case 1):

object Main extends App {
  implicit val c = new ConcreteContext()

  import c._

  implicitly[Encoder].write()
}

then it compiles and prints Test.

But when I try to call the same code inside singleton object (case 2):

object TestObject {
  def apply()(implicit c: ConcreteContext): Unit = {
    import c._
    implicitly[Encoder].write()
  }
}

object Main extends App {
  implicit val c = new ConcreteContext()

  TestObject()
}

compilation fails with:

path/to/Main.scala:29: could not find implicit value for parameter e: c.Encoder implicitly[c.Encoder].write()

If I change (case 3):

implicit val e = new AsyncEncoder()

to

implicit val e: Encoder = new AsyncEncoder()

then it compiles and runs as expected.

But for some reason this is not acceptable for me.

Why does compilation fail in the above case?


Solution

  • As it said in comments there is no issue in Scala 2.12.0.

    For Scala 2.11.8 I used the following workaround assuming that Encoder will have only one method:

    trait Context {
      implicit val e: Encoder
    
      type BaseEncoder = () => Unit
    
      type Encoder <: BaseEncoder
    }
    
    trait AsyncEncoders {
      this: Context =>
    
      type Encoder = AsyncEncoder
    
      class AsyncEncoder extends BaseEncoder {
        override def apply(): Unit = {
          println("Test")
        }
      }
    
      implicit val e = new AsyncEncoder()
    }
    
    class ConcreteContext extends Context with AsyncEncoders {
    }
    
    object TestObject {
      def apply()(implicit c: ConcreteContext): Unit = {
        import c._
        implicitly[Encoder].apply()
      }
    }
    
    object Main extends App {
      implicit val c = new ConcreteContext()
    
      TestObject()
    }