最终编辑:所有角落案例都已解决,唯一的问题是我必须从Circe复制私有/me
方法才能使Encoder.encodeTraversableOnce
正常工作。我还必须更改Encoder
以使用MyCollection
而不仅仅是TraversableOnce
(这是因为Traversable
仅适用于Encoder
TraversableOnce
与Decoder
一起使用。演示所有案例的小提琴可以在https://scalafiddle.io/sf/F5Qo8cn/15找到。
小提琴可以在https://scalafiddle.io/sf/F5Qo8cn/8
找到基本上我试图在集合类型上进行抽象,这是在包含可遍历集合的模型的上下文中,即假设我们有以下
Traversable
这允许我们使用特定的集合类型实例化case class MyCollection[C[A] <: Traversable[A]](stuff: C[String])
,即
MyCollection
val innerV = MyCollection(Vector("a"))
val innerL = MyCollection(List("b"))
也恰好具有一个具体类型,因此当我们访问MyCollection
方法时,它将返回我们用来创建它的类型(即.stuff
的情况其innerV
与Vector
的{{1}}
由于这是Web框架的上下文,innerL
碰巧代表了一些JSON,因此使用Circe 0.9.1我们可以编写如下解码器
List
请注意,我们会明确调用MyCollection
参数以及手动编写解码器,以便我们可以帮助跟踪隐含问题的位置。我们的想法是,我们可以通常使用我们想要的任何集合类型来实例化object MyCollection {
implicit def decoder[C[A] <: Traversable[A]]: Decoder[MyCollection[C]] = {
new Decoder[MyCollection[C]] {
override def apply(c: HCursor) = {
c.downField("stuff").as[C[String]](Decoder.decodeTraversable[String, C](
implicitly,
implicitly
))
}.map((x: C[String]) => MyCollection.apply(x))
}
}
}
,即
implicit
问题是我得到了一个不同的隐式扩展,特别是在这一行
case class
我们得到的错误是
ScalaFiddle.scala:19:错误:含糊不清的隐含值:两者都是getter StringCanBuildFrom在模块类Predef中的type =&gt; generic.this.CanBuildFrom [String,scala.this.Char,String]和方法 $符合模块类Predef的类型[A] =&gt; $ $少结肠$更少[A,A] 匹配预期类型T. 隐式 ^
有谁知道造成这个问题的原因
答案 0 :(得分:2)
C
的上限过于松散:在方法体内,编译器对C
一无所知,只是它是Traversable[A]
,因此无法自动证明有一个CanBuildFrom[Nothing, A, C[A]]
的实例。
简单的解决方法是从外部提供CanBuildFrom[Nothing, A, C[A]]
,因为这些东西很容易在使用网站上生成(因为它显然可以用于List
和{等具体实现{1}}):
Vector
编辑:以下提案无效,正如@OlegPyzhcov所指出的那样,因为我们没有// Start writing your ScalaFiddle code here
import io.circe._
import io.circe.syntax._
import scala.collection.generic.CanBuildFrom
case class MyCollection[C[A] <: Traversable[A]](stuff: C[String])
val innerV = MyCollection(Vector("a")).stuff
val innerL = MyCollection(List("b")).stuff
object MyCollection {
implicit def decoder[C[A] <: Traversable[A]]
(implicit cbf: CanBuildFrom[Nothing, String, C[String]])
: Decoder[MyCollection[C]] = {
new Decoder[MyCollection[C]] {
override def apply(c: HCursor) = {
c.downField("stuff").as[C[String]](Decoder.decodeTraversable[String, C](
implicitly,
// this thing cannot be generated
// if you know nothing about `C` except
// that it is a `Traversable[A]`
cbf
))
}.map((x: C[String]) => MyCollection.apply(x))
}
}
}
def getMyCollection[C[A] <: Traversable[A]]
(implicit cbf: CanBuildFrom[Nothing, String, C[String]])
: MyCollection[C] = {
val jsonString = """{ "stuff": ["a","b"] }"""
val json = io.circe.parser.parse(jsonString).right.get
json.as[MyCollection[C]].right.get
}
// cbf is supplied by compiler, it is trivial to
// generate here, because you know that you can do it
// for lists and vectors
def asVector: MyCollection[Vector] = getMyCollection[Vector]
def asList: MyCollection[List] = getMyCollection[List]
println(asVector)
println(asList)
的实例可以调用GenTraversable
。我会留在这里,以防我突然想起我在想什么。
我能想到的另一个解决方案是收紧上限
companion
,然后查看GenTraversable[A]
companion
,并使用GenericCompanion
方法构建所需的CanBuildFrom[Nothing, String, C[String]]
。权衡将是:这将改变界限
newBuilder
要更紧Traversable[A]
,但你可以 放弃恼人的GenTraversable[A]
- 隐含。
答案 1 :(得分:1)
注意:Scala编译器选项C[_]
有时可以帮助您了解发生了什么(虽然这种情况 非常奇怪!)。
您在范围内没有隐含的CBF。您需要从顶级传递它,其中已知具体类型implicit def decoder[C[A] <: Traversable[A]](implicit cbf: CanBuildFrom[Nothing, String, C[String]]): Decoder[MyCollection[C]]
。所以修复是:
implicitly
如果您删除Cannot construct a collection of type C[String] with elements of type String based on a collection of type Nothing.
not enough arguments for method decodeTraversable: (implicit decodeA: io.circe.Decoder[String], implicit cbf: scala.collection.generic.CanBuildFrom[Nothing,String,C[String]])io.circe.Decoder[C[String]].
Unspecified value parameter cbf.
语句,则会收到一个不同的错误,该错误应该暗示您错过了它。
expected type T
接下来的部分主要是假设,所以请耐心等待。也许一些Scala编译器黑客会纠正我。
触发错误的答案取决于T
的来源。 Decoder
中的CanBuildFrom
不在 @inline def implicitly[T](implicit e: T) = e
中,也不在implicitly
中。但它用于scala.Predef:
CanBuildFrom
因此,当您使用Decoder.decodeTraversable
时,编译器可以尝试两件事:
CanBuildFrom
CanBuildFrom
的实例
implicit e: T
第一步失败(没有StringCanBuildFrom
)。但它还没有结束。所以编译器试图找到$conforms
而不对类型T有任何约束。它找到了多个东西:
implicitly
因此,一旦发现有不同的含义,它会立即退出,给您的错误信息不足。
使用修复我建议使用{{1}}或不同意,因为它在第一步结束。