10

鉴于以下关于 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

它在明确拼写时起作用String @@ Foo

@ val x: String @@ Foo = tag[Foo][String]("foo") 
x: String @@ Foo = "foo"

@ implicitly[Decoder[String @@ Foo]] 
res10: Decoder[String @@ Foo] = io.circe.Decoder$$anon$21@2b17bb37

但是,在定义类型别名时:

@ type FooTypeAlias = String @@ Foo 
defined type FooTypeAlias

它没有编译:

@ implicitly[Decoder[FooTypeAlias]] 
cmd12.sc:1: diverging implicit expansion for type io.circe.Decoder[ammonite.$sess.cmd11.FooTypeAlias]
starting with method decodeTraversable in object Decoder
val res12 = implicitly[Decoder[FooTypeAlias]]
                      ^
Compilation Failed

这是为什么?有已知的“修复”吗?

4

1 回答 1

10

幸运的是,您在同一天遇到了两个编译器错误。这个是scala/bug#8740。好的?消息是,此评论中有一个部分修复等待有人站出来进行 PR(也许这就是你)。我相信它是部分的,因为它看起来适用于特定标签,但不适用于通用标签(我不是 100% 确定)。

你看到一个发散的隐式扩展的原因真的很有趣。编译器可以在一个步骤中展开所有别名(基本上从 开始FooTypeAlias |= String with Tagged[Foo])或不展开任何东西。因此,当它比较String @@ Foo并且A @@ B它不扩展时,因为它们按原样匹配。但是,当它比较FooTypeAlias并且A @@ B它完全扩展时,它最终会出现必须比较精炼类型的情况,其中一个包含类型变量(请参阅我对您其他相关问题的回答)在这里,我们精心设计的抽象再次崩溃,约束的顺序开始变得重要。你作为程序员,看着就A with Tagged[B] <:< String with Tagged[Foo]知道最好的匹配是A =:= Stringand B =:= Foo。然而 Scala 将首先比较A <:< StringA <:< Tagged[Foo]得出结论A <:< Tagged[Foo] with String(是的,相反)它离开NothingB。但是等等,我们需要一个隐式的Decoder[A]!- 循环发送给我们。所以A得到了过度约束和B约束不足。

编辑:@@如果我们进行抽象以防止编译器去锯齿,它似乎有效: milessabin/shapeless#807。但现在它装箱了,我无法让数组工作。

于 2018-01-12T23:25:21.840 回答