Mill Hosper
Mill Hosper

Reputation: 49

Scala code will not pick up implicit class instance

Why is the implicit not being picked up automatically?? implicit class is not picked up by default as evidence in the code below.

object temp {
  def main(args: Array[String]): Unit = {
    case class Foo(str: String)

    trait Special[A] {
      def getStr: String
    }

    implicit class SpecialFoo(x: Foo) extends Special[Foo] {
      def getStr = x.str
    }

    /* For the above SpecialFoo implicit class, I think the following is replaced by the compiler:
     * --------------------------------------------------------------------------------------------
       class SpecialFooClass(x: Foo) {
         def getStr: String = x.str
       }
       implicit def specialFooFunction(x: Foo): SpecialFooClass = new SpecialFooClass(x);
     * --------------------------------------------------------------------------------------------
     */

    def specialFunction[T: Special](thing: T): String = implicitly[Special[T]].getStr;

    // val wrapped = specialFunction(Foo("hi")) // Will not pick up the implicit SpecialFoo class, why??

    val foo = Foo("hi")
    val wrapped1: String = specialFunction(foo)(SpecialFoo(foo)) // But giving the evidence explicitly works!

    println(wrapped1)
  }
}

Upvotes: 1

Views: 286

Answers (2)

bottaio
bottaio

Reputation: 5093

Although @francoisr answer is a valid answer it seems like you would like to be able to pass an object to a method that implements Special[A] interface. Your code will compile for the following definition of specialFunction.

def specialFunction[T](thing: T)(implicit toSpecial: T => Special[T]): String = thing.getStr

This expresses your need to convert type T to a Special[T] and thus you are able to call .getStr method.

Upvotes: 2

francoisr
francoisr

Reputation: 4595

It looks like you're mixing up two concepts here.

On one hand, there is implicit conversion, which means the compiler will convert a value of type Foo into a value of type Special[Foo] whenever it needs to. That's indeed what your implicit class declares.

On the other hand, the

def specialFunction[T: Special](thing: T): String

is a shortcut for

def specialFunction[T](thing: T)(implicit ev: Special[T]): String

This means the specialFunction method takes two arguments. The second argument is implicit, which means that if you don't pass it explicitly, the compiler will try to find it in the implicit scope. However, here you don't have an implicit Special[Foo] in scope. What you have is an implicit way to convert Foo into Special[Foo].

The pattern you're trying to use here is very common, but it's usually achieved in another way:

  trait Special[A] {
    def getStr(a: A): String
  }

  case class Foo(str: String)
  object Foo {
    implicit val special: Special[Foo] = new Special[Foo] {
      override def getStr(a: Foo) = a.str
    }
  }

  def specialFunction[T: Special](thing: T): String = 
    implicitly[Special[T]].getStr(thing)

  val foo = Foo("hi")
  println(specialFunction(foo))

Here, we don't wrap Foo into Special[Foo], but instead treat Special[A] as a separate entity that knows how to extract a String from an A.

Note: If you're using Scala 2.12, you can express val special in a very concise way thanks to support for single abstract method (traits or abstract classes that only consist of a single abstract method, like Special[A] only has getStr):

implicit val special: Special[Foo] = _.str

Upvotes: 2

Related Questions