解码带标记类型的案例类



给定:

鉴于菊石如下:

@ 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

给定一个Foo

@ case class F(x: String @@ Foo)  
defined class F

我可以召唤一个Decoder[String @@ Foo]

@ Decoder[String @@ Foo] 
res17: Decoder[String @@ Foo] = io.circe.Decoder$$anon$21@16b32e49

但不是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

如何获得Decoder[F]

这是无形Lazy中的一个错误 - milessabin/shapeless#309

我有一个 PR 可以编译您的示例 - milessabin/shapeless#797(我检查了publishLocal)

基本上Lazy的问题是它过于急切地扩展类型别名(A @@ BA with Tagged[B]的类型别名),这反过来又触发了 Scala 错误 - scala/bug#10506

Scala错误没有明确的解决方案。这是子类型与参数多态性问题的另一个化身,使类型推断复杂化。它的要点是Scala必须同时执行子类型检查和类型推断。但是,当我们将一些类型变量(如AB)放入像A with Tagged[B]这样的精炼类型中时(实际上 circe 最终会寻找一个FieldType[K, A with Tagged[B]],其中FieldType是隐藏精炼类型的另一个类型别名),必须单独检查每个组件的子类型。这意味着我们选择检查组件的顺序决定了类型变量AB将如何约束。在某些情况下,它们最终会过度约束或约束不足,无法正确推断。

Apropo,无形测试显示了一种解决方法,但我认为它不适用于 circe,因为它使用某种宏而不是进行香草类型类派生。

长话短说,您可以:

  1. 等待无形(请点赞#797)和随后的circe发布
  2. 不使用标记类型 =/
  3. 尝试使用不同的编码,没有精炼或结构化类型 - 也许是alexknvl/newtypes?(我没试过)

最新更新