解码带有标记类型的案例类 [英] Decoding Case Class w/ Tagged Type
问题描述
给定:
鉴于 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`@ 导入 shapeless.tag导入 shapeless.tag@特质福定义特征 Foo@ 导入 io.circe._, io.circe.generic.semiauto._导入 io.circe._, io.circe.generic.semiauto._@import shapeless.tag.@@导入 shapeless.tag.@@@implicit def taggedTypeDecoder[A, B](implicit ev: Decoder[A]): Decoder[A @@ B] =ev.map(标签[B][A](_))定义函数 taggedTypeDecoder
给定一个 Foo
:
@case class F(x: String @@Foo)定义类 F
我可以召唤一个Decoder[String @@Foo]
:
@解码器[字符串@@Foo]res17:解码器[字符串@@ Foo] = io.circe.Decoder$$anon$21@16b32e49
但不是F
:
@deriveDecoder[F]cmd18.sc:1: 找不到 io.circe.generic.decoding.DerivedDecoder[ammonite.$sess.cmd16.F] 类型的惰性隐式值val res18 = 派生解码器 [F]^编译失败
如何获得Decoder[F]
?
This is a bug in shapeless' Lazy
- milessabin/shapeless#309
我有一个 PR 可以让您的示例编译 - milessabin/shapeless#797(我检查了 publishLocal
)
基本上 Lazy
的问题在于它过于急切地扩展类型别名(A @@ B
是 A 的类型别名,带有 Tagged[B]
) 进而触发 Scala 错误 - scala/bug#10506>
Scala 错误没有明确的解决方案.这是使类型推断复杂化的子类型与参数多态性问题的另一个化身.其要点是 Scala 必须同时执行子类型检查和类型推断.但是,当我们将诸如 A
和 B
之类的类型变量放入诸如 A with Tagged[B]
之类的精炼类型中时(实际上 circe 最终会寻找a FieldType[K, A with Tagged[B]]
其中 FieldType
是另一个隐藏精炼类型的类型别名),必须单独检查每个组件的子类型.这意味着我们选择检查组件的顺序决定了如何约束类型变量 A
和 B
.在某些情况下,它们最终会受到过度约束或约束不足,无法正确推断.
Apropo,无形测试显示 解决方法,但我认为它不适用于 circe,因为它使用某种宏而不是进行普通类型类派生.
长话短说,您可以:
- 等待一个无形的(请upvote #797)和随后的circe发布立>
- 不使用标记类型 =/
- 尝试使用不同的编码而不使用精炼或结构类型 - 也许是 alexknvl/newtypes?(我没试过)
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]
?
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)
这篇关于解码带有标记类型的案例类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!