Achraf Dawny jr.
Achraf Dawny jr.

Reputation: 186

Typeclass ops : Enable to find implicit value for parameter

i'm new to typeclass pattern and implicit in scala, below is a code snippet i did to test this pattern, but i don't know why the compareB method is not able to find implicit value for the parameter wrapper , even so i have an implicit field of type IntWrapper[Int] declared.

Does anyone have an idea how implicit are resolved in typeclass ? and why the code below does not compile ?

Thanks

trait IntWrapper[T]{
  def test: Boolean
}

trait ProductComparator[T] extends Serializable{
  def compare(p1 : T, p2: T): Boolean
}

object ProductComparator{

  def apply[A](implicit cmp: ProductComparator[A]) : ProductComparator[A] = cmp


  implicit def w: IntWrapper[Int] =
    new IntWrapper[Int] {
      override def test: Boolean = true
    }

  implicit def CompareB[T <: Product, Repr <: HList, KRepr <: HList](implicit gen: LabelledGeneric.Aux[T, Repr], keys: Keys.Aux[Repr, KRepr], wrapper:  IntWrapper[Int]) : ProductComparator[T] =
      new ProductComparator[T] {
        override def compare(p1: T, p2: T): Boolean = {
          p1.productArity == p2.productArity
        }
      }

}


case class Emp(a: Int, b: Int)

object Demo extends App{
  val comparator = implicitly[ProductComparator[Emp]]
}

Upvotes: 0

Views: 205

Answers (2)

Dmytro Mitin
Dmytro Mitin

Reputation: 51658

Normally implicits for type TC[A] are put either to the companion object of TC or companion object of A.

So transfer

implicit def w: IntWrapper[Int] =
  new IntWrapper[Int] {
    override def test: Boolean = true
  }

from the companion object of ProductComparator to the companion object of IntWrapper

object IntWrapper {
  implicit def w: IntWrapper[Int] =
    new IntWrapper[Int] {
      override def test: Boolean = true
    }
}

Then the code should compile.

Alternatively you can import w as @TomerShetah proposed.

Where does Scala look for implicits?

When you define

implicit val foo: Foo = ???

def bar(implicit foo1: Foo) = ???

(in your example foo is w, bar is CompareB, Foo is IntWrapper[Int]) you shouldn't generally assume that foo1 is foo. foo is defined in the current scope and foo1 will be resolved in the scope of bar call site. This can be foo if it's there in a scope or some other implicit.

When doing implicit resolution with type parameters, why does val placement matter?

Setting abstract type based on typeclass

Reverse HList and convert to class?

Upvotes: 4

Tomer Shetah
Tomer Shetah

Reputation: 8529

The issue is scopes. In the line:

val comparator = implicitly[ProductComparator[Emp]]

The method:

def apply[A](implicit cmp: ProductComparator[A]): ProductComparator[A] = cmp

Will be called, where A is Emp. The implicit that can create that, is CompareB. CompareB Needs more implicits. The first 2, comes from imports. So they are in scope. The variable w is defined on the object ProductComparator therefore it is not in the scope where you define val comparator.

In order to add it to scope, you have few options:

  1. Importing it with:

    import ProductComparator.w
    
  2. Moving implicit def w: IntWrapper[Int] to the same scope as trait IntWrapper[T], which makes:

import shapeless.ops.record.Keys
import shapeless.{HList, LabelledGeneric}

trait IntWrapper[T]{
  def test: Boolean
}

implicit def w: IntWrapper[Int] =
  new IntWrapper[Int] {
    override def test: Boolean = true
  }

trait ProductComparator[T] extends Serializable{
  def compare(p1 : T, p2: T): Boolean
}

object ProductComparator{

  def apply[A](implicit cmp: ProductComparator[A]) : ProductComparator[A] = cmp

        implicit def CompareB[T <: Product, Repr <: HList, KRepr <: HList](implicit gen: LabelledGeneric.Aux[T, Repr], keys: Keys.Aux[Repr, KRepr], wrapper:  IntWrapper[Int]) : ProductComparator[T] =
          new ProductComparator[T] {
            override def compare(p1: T, p2: T): Boolean = {
              p1.productArity == p2.productArity
            }
          }

}

case class Emp(a: Int, b: Int)

object Demo extends App{
  val comparator = implicitly[ProductComparator[Emp]]
}

To read more about where does Scala look for implicits you can read Daniels brilliant post.

Upvotes: 2

Related Questions