Alex Abdugafarov
Alex Abdugafarov

Reputation: 6432

Scala typeclasses implicit resolution

(Scala 2.11.8)

Consider the following code:

object ScalaTest extends App {
  class Wrapper {
    import Wrapper._

    def init(): Unit = {
      // "could not find implicit value for parameter tc: ScalaTest.Wrapper.TC[Int]"
      printWithTC(123)

      // Compiles
      printWithTC(123)(IntTC)

      // Compiles again!
      printWithTC(132)
    }
  }

  object Wrapper {
    trait TC[A] {
      def text(a: A): String
    }

    implicit object IntTC extends TC[Int] {
      override def text(a: Int) = s"int($a)"
    }

    def printWithTC[A](a: A)(implicit tc: TC[A]): Unit = {
      println(tc.text(a))
    }
  }

  (new Wrapper).init()
}

I have a bunch of questions regarding this piece of code:

  1. Why doesn't IntTC get resolved in the first place?
  2. Why it compiles after being used once? (if you comment out the first invocation, code works)
  3. Where should typeclass implicits be placed to get resolved properly?

Upvotes: 5

Views: 75

Answers (2)

Jasper-M
Jasper-M

Reputation: 15086

Use a val with a explicit return type. See https://github.com/scala/bug/issues/801 and https://github.com/scala/bug/issues/8697 (among others).
Implicit objects have the same issue as implicit vals and defs with inferred return types. As for your second question: when IntTC is used explicitly you force the compiler to typecheck it, so after that point its type is known and can be found by implicit search.

class Wrapper {
  import Wrapper._

  def init(): Unit = {
    // Compiles
    printWithTC(123)

    // Compiles
    printWithTC(123)(IntTC)

    // Compiles
    printWithTC(132)
  }
}

object Wrapper {
  trait TC[A] {
    def text(a: A): String
  }

  implicit val IntTC: TC[Int] = new TC[Int] {
    override def text(a: Int) = s"int($a)"
  }

  def printWithTC[A](a: A)(implicit tc: TC[A]): Unit = {
    println(tc.text(a))
  }
}

If you really want your implicit to be evaluated lazily like an object, you can use an implicit lazy val with an explicit type.

Upvotes: 3

Stephen
Stephen

Reputation: 4296

Define the implicit before the use of it.

object Wrapper {
  trait TC[A] {
    def text(a: A): String
  }

  implicit object IntTC extends TC[Int] {
    override def text(a: Int) = s"int($a)"
  }

  def printWithTC[A](a: A)(implicit tc: TC[A]): Unit = {
    println(tc.text(a))
  }
}

class Wrapper {
  import Wrapper._

  def init(): Unit = {
    // "could not find implicit value for parameter tc: ScalaTest.Wrapper.TC[Int]"

    printWithTC(123)

    // Compiles
    printWithTC(123)(IntTC)

    // Compiles again!
    printWithTC(132)
  }
}

(new Wrapper).init()

Upvotes: 0

Related Questions