问题
Given:
Given the following on Ammonite:
@ import $ivy.`io.circe::circe-core:0.9.0`
@ import $ivy.`io.circe::circe-generic:0.9.0`
@ import $ivy.`com.chuusai::shapeless:2.3.3`
@ import shapeless.tag
import shapeless.tag
@ trait Foo
defined trait Foo
@ import io.circe._, io.circe.generic.semiauto._
import io.circe._, io.circe.generic.semiauto._
@ import shapeless.tag.@@
import shapeless.tag.@@
@ implicit def taggedTypeDecoder[A, B](implicit ev: Decoder[A]): Decoder[A @@ B] =
ev.map(tag[B][A](_))
defined function taggedTypeDecoder
Given a Foo
:
@ case class F(x: String @@ Foo)
defined class F
I can summon an Decoder[String @@ Foo]
:
@ Decoder[String @@ Foo]
res17: Decoder[String @@ Foo] = io.circe.Decoder$$anon$21@16b32e49
But not a F
:
@ deriveDecoder[F]
cmd18.sc:1: could not find Lazy implicit value of type io.circe.generic.decoding.DerivedDecoder[ammonite.$sess.cmd16.F]
val res18 = deriveDecoder[F]
^
Compilation Failed
How can I get a Decoder[F]
?
回答1:
This is a bug in shapeless' Lazy
- milessabin/shapeless#309
I have a PR that makes your example compile - milessabin/shapeless#797 (I checked with publishLocal
)
Basically the problem in Lazy
is that it expands type aliases too eagerly (A @@ B
is a type alias for A with Tagged[B]
) which in turn triggers a Scala bug - scala/bug#10506
The Scala bug doesn't have a clear solution in sight. It's another incarnation of the subtyping vs parametric polymorphism problem that complicates type inference. The gist of it is that Scala has to perform subtype checking and type inference at the same time. But when we put some type variables like A
and B
in a refined type like A with Tagged[B]
(actually circe ends up looking for a FieldType[K, A with Tagged[B]]
where FieldType
is yet another type alias hiding a refined type), subtyping has to be checked for each component individually. This means that the order in which we choose to check the components determines how the type variables A
and B
will be constrained. In some cases they end up over- or under-constrained and cannot be inferred correctly.
Apropo, the shapeless tests show a workaround, but I don't think it applies to circe, because it's using some kind of macro rather than doing vanilla typeclass derivation.
Long story short you can:
- Wait for a shapeless (please upvote #797) and subsequent circe release
- Not use tagged types =/
- Try to use a different encoding without refined or structural types - maybe alexknvl/newtypes? (I haven't tried)
来源:https://stackoverflow.com/questions/48174799/decoding-case-class-w-tagged-type