0

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]]
}
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • In `CompareB` you don't actually use any of implicits `gen: LabelledGeneric.Aux[T, Repr], keys: Keys.Aux[Repr, KRepr], wrapper: IntWrapper[Int]`. So I guess it's just for example. Right? – Dmytro Mitin Oct 27 '20 at 12:04

2 Answers2

4

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?

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
2

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.

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
  • I dont want to create ProductComparator[Emp] manually, actualy the implicit def CompareB would create it for me, my question is , why the compareB is not able to find wrapper: IntWrapper[Int] parameter in her scope ? please note that when i remove this parameter the code compiles – Achraf Dawny jr. Oct 27 '20 at 11:07
  • The param to remove is {wrapper: IntWrapper[Int]} – Achraf Dawny jr. Oct 27 '20 at 11:28
  • Right. So you do even need it? The issue here is scoping. You need to `import ProductComparator.w` in your main application, and then it will find it. – Tomer Shetah Oct 27 '20 at 11:32
  • 1
    @TomerShetah *"The first 2, comes from imports. So they are in scope."* Actually, **implicits** `LabelledGeneric` and `Keys` do not come from imports. The imports of `LabelledGeneric` and `Keys` bring their names to the local scope, not the implicits themselves. The implicits for `LabelledGeneric` and `Keys` are in **implicit scope** because they are defined in the companion objects of `LabelledGeneric` and `Keys`. – Dmytro Mitin Oct 27 '20 at 12:43