Reputation: 186
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
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
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:
Importing it with:
import ProductComparator.w
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