I'm trying to make a generic decoder that decodes fields differently depending on shapeless.Tags declared on those fields. To that end, I had an implicit defined as follows which can turn any decoder for B into a decoder for B @@ Id (A is the input type).
implicit def idTagDecoder[A, B](implicit dec: Lazy[Decoder[A, B]]): Decoder[A, B @@ Id] =
dec.value.decode(a).map(tag[Id](_))
When I run a test and a Decoder produced by this is used, I get a StackOverflowError indicating that Scala is satisfying the dec parameter with the generated decoder! If I leave off the Lazy, that doesn't happen, but I get "diverging implicit expansion."
So, I modified the code just to make sure this is what's going on. Now, my definition looks like this.
implicit def idTagDecoder[A, B](implicit dec: Lazy[Decoder[A, B]]): Decoder[A, B @@ Id] =
new Decoder[A, B @@ Id] {
override def decode(a: A): DecodeResult[B @@ Id] = {
// These are different types, so the compiler shouldn't try to satisfy `dec` with `this`, but it does!
require(dec.value != this)
dec.value.decode(a).map(tag[Id](_))
}
}
That gives me a "requirement failed" at run-time, as expected.
The compiler knows better than this. If I try to do what it's doing explicitly to produce a Decoder[JAny, UUID @@ Id], it knows it's the wrong type and fails to compile.
val d: Decoder[JAny, UUID @@ Id] = Id.idTagDecoder(Lazy(Id.idTagDecoder(Decoder.uuidDecoder)))
Is this somehow the expected behavior? If so, how should I do what I'm trying to do? If not, does anyone know how to work around this?
I tried to get a small example that demonstrates this problem to no avail. It works like I would expect on anything simpler than my use case. You can see the code and run the failing test here (with sbt test).
I'm using shapeless 2.3.3 and scala 2.12.13.
Thanks!
Update: Given that this looks like a bug in the Scala compiler, I went ahead and tested it against Scala 2.13.5 and the problem is still there.