解码具有标记类型的案例类

时间:2018-01-09 18:46:58

标签: scala shapeless circe

假设:

鉴于以下有关亚扪人的信息:

@ 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]

1 个答案:

答案 0 :(得分:7)

这是无形'Lazy - milessabin/shapeless#309

中的错误

我有一个PR让您的示例编译 - milessabin/shapeless#797(我使用publishLocal进行了检查)

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

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

Apropo,无形测试显示workaround,但我不认为它适用于circe,因为它使用的是某种宏,而不是使用vanilla类型派生。

长话短说,你可以:

  1. 等待一个无形的(请upvote #797)和随后的circe释放
  2. 不使用标记类型= /
  3. 尝试使用不同精简或结构类型的不同编码 - 也许alexknvl/newtypes? (我还没试过)