Reputation: 1570
I want to derive instances of type classes from unary case classes. But when i try to implicitly derive it i always get an error message. If i derive it explicitly using implicit method - it works. I'm not sure, but maybe the reason is that i missed some implicit types in my function
import shapeless._
import scala.reflect.ClassTag
import scala.reflect.runtime.universe.TypeTag
sealed trait Foo[A] {
def hello(): Unit
}
object Foo {
def apply[A](implicit foo: Foo[A]): foo.type = foo
def instance[A](implicit tag: ClassTag[A]): Foo[A] = new Foo[A] {
override def hello(): Unit = println(s"Hello from ${tag.runtimeClass.getName}")
}
}
trait Instances extends LowestPriority {
implicit val intHelloInstance: Foo[Int] = Foo.instance[Int]
}
trait LowestPriority {
implicit def derive[A: TypeTag, L <: HList, H](
implicit gen: Generic.Aux[A, L],
H: Lazy[Foo[H]],
isUnary: (H :: HNil) =:= L
): Foo[A] =
new Foo[A] {
override def hello(): Unit = {
print(s"Derived: ")
H.value.hello()
}
}
}
object T extends Instances {
case class A(a: Int)
def main(args: Array[String]): Unit = {
intHelloInstance.hello()
// val a: Foo[A] = derive[A, Int :: HNil, Int] // this works
// a.hello()
Foo[A].hello() // error
}
}
From logs:
Information:(45, 8) shapeless.this.Generic.materialize is not a valid implicit value for shapeless.Generic.Aux[H,L] because: hasMatchingSymbol reported error: H is not a case class, case class-like, a sealed trait or Unit Foo[A].hello()
How can i fix it?
Upvotes: 1
Views: 420
Reputation: 27535
This is one of the cases when behavior depends on... the order of implicits to resolve.
If you modify signature to:
implicit def derive[A, L <: HList, H](
implicit gen: Generic.Aux[A, L],
isUnary: (H :: HNil) =:= L, // swapped
H: Lazy[Foo[H]] // with this
): Foo[A] = ...
compiler will:
HList
L
that could be paired with A
L
equal to some H :: HNil
figuring out H
in the processH
to fetch Lazy[Foo[H]]
and you will successfully compile Foo[A].hello()
.
When these two last argument are swapped to what you have in your question, compiler has to
H
(which in our test case most likely WON"T be Int)L
to match itGeneric
which now is forced to prove that A
is representable by some H :: HNil
where H
is most likely not Int
and failing to do it but with a misguiding error informationThis is one of these cases that shows that shapeless based derivation is sometimes tricky, and also here it shows that shapeless authors assumed in macros that the most likely cause of macro expansion failure is that A
was not case class, while as we just saw it might be also compiler forcing some impossible proof because type inference got things wrong because we resolved them in wrong order.
Upvotes: 1