找不到参数 enc 的隐式值:CsvEncoder[Shape]

Posted

技术标签:

【中文标题】找不到参数 enc 的隐式值:CsvEncoder[Shape]【英文标题】:could not find implicit value for parameter enc: CsvEncoder[Shape] 【发布时间】:2017-10-12 09:39:02 【问题描述】:

我正在尝试理解无形的副产品并有以下示例,但不起作用:

import shapeless.HList, ::, HNil
import shapeless.Generic
import shapeless.Coproduct, :+:, CNil, Inl, Inr

trait CsvEncoder[A] 
  def encode(value: A): List[String]



sealed trait Shape

final case class Rectangle(width: Double, height: Double) extends Shape

final case class Circle(radius: Double) extends Shape

object CsvEncoder 

  def createEncoder[A](func: A => List[String]): CsvEncoder[A] = 
    new CsvEncoder[A] 
      override def encode(value: A): List[String] = func(value)
    
  

  implicit val booleanEncoder: CsvEncoder[Boolean] =
    createEncoder(value => if (value) List("yes") else List("no"))

  implicit val intEncoder: CsvEncoder[Int] =
    createEncoder(value => List(value.toString))

  implicit val stringEncoder: CsvEncoder[String] =
    createEncoder(value => List(value))

  implicit val doubleEncoder: CsvEncoder[Double] =
    createEncoder(value => List(value.toString))


  def apply[A](implicit enc: CsvEncoder[A]): CsvEncoder[A] = enc

  implicit val cnilEncoder: CsvEncoder[CNil] =
    createEncoder(cnil => throw new Exception("Inconceivable!"))

  implicit def coproductEncoder[H, T <: Coproduct](
                                                    implicit
                                                    hEncoder: CsvEncoder[H],
                                                    tEncoder: CsvEncoder[T]
                                                  ): CsvEncoder[H :+: T] = createEncoder 
    case Inl(h) => hEncoder.encode(h)
    case Inr(t) => tEncoder.encode(t)
  

  def writeCsv[A](values: List[A])(implicit enc: CsvEncoder[A]): String =
    values.map(value => enc.encode(value).mkString(",")).mkString("\n")




object Main 


  def main(args: Array[String]) 

    println("----------------------------------------------------------")
    val shapes: List[Shape] = List(
      Rectangle(3.0, 4.0),
      Circle(1.0)
    )
    println(CsvEncoder.writeCsv(shapes))

  


编译器抱怨:

Error:(162, 32) could not find implicit value for parameter enc: CsvEncoder[Shape]
    println(CsvEncoder.writeCsv(shapes))
Error:(162, 32) not enough arguments for method writeCsv: (implicit enc: CsvEncoder[Shape])String.
Unspecified value parameter enc.
    println(CsvEncoder.writeCsv(shapes))

我是否为Shape 创建了CsvEncoder 的实例? Shapeless 应该照顾我,还是?

【问题讨论】:

【参考方案1】:

我建议重新/阅读The Type Astronaut's Guide to Shapeless Book,这确实是一本了不起的书。您发布的代码似乎部分来自那里。


CSV 编码器示例使用 Employee,并手动写入 CsvEncoder(第 23 页):

implicit val iceCreamEncoder: CsvEncoder[IceCream] =
  new CsvEncoder[IceCream] 
    def encode(i: IceCream): List[String] =
      List(
        i.name,
        i.numCherries.toString,
        if(i.inCone) "yes" else "no"
      )
  

如果您确实想要手动编写所有这些,那么您将需要更多样板。


这在本书第 27 页的 3.2.1 中有所描述:

import shapeless.HList, ::, HNil
implicit val hnilEncoder: CsvEncoder[HNil] =
  createEncoder(hnil => Nil)
implicit def hlistEncoder[H, T <: HList](
  implicit
  hEncoder: CsvEncoder[H],
  tEncoder: CsvEncoder[T]
): CsvEncoder[H :: T] =
  createEncoder 
    case h :: t =>
      hEncoder.encode(h) ++ tEncoder.encode(t)
  

我们有递归情况hlistEncoder(返回CsvEncoder[H :: T]),它接受头部和尾部,以及基本情况编码器hnilEncoder(返回CsvEncoder[HNil],因为HList总是有一个HNil 在它的末尾,就像所有其他链表一样)。

然而,这足以对任何 HList 进行 CSV 编码,但 Rectangle 不是 HList!您需要使用Generic 来回转换。这在本书的 3.2.2 中进行了解释,就在上一部分之后(我将代码转换为使用 Generic.Aux,这是 shapeless 最重要的工具之一):

implicit def genericEncoder[A, R](
  implicit
  gen: Generic.Aux[A, R],
  enc: CsvEncoder[R]
): CsvEncoder[A] =
  createEncoder(a => enc.encode(gen.to(a)))

gen.toa(比如Rectangle)转换为HList(即Double :: Double :: HNil,并对其进行编码。 它还将Shape 转换为Rectangle :+: Circle :+: CNil,因此您的Main 可以按原样工作:)。

【讨论】:

这个解决方案已经行不通了。我刚试过!你会再检查一次吗?我的 scala 版本是 2.13.6,我的 shapeless 是 ""com.chuusai" %% "shapeless" % "2.3.3""

以上是关于找不到参数 enc 的隐式值:CsvEncoder[Shape]的主要内容,如果未能解决你的问题,请参考以下文章

找不到参数元组的隐式值

找不到参数 e 的隐式值

Scala 类型参数化,Shapeless - 找不到参数 Generic 的隐式值

找不到参数的隐式值

找不到参数 flash 的隐式值

akka-http:找不到参数解组的隐式值