找不到参数 Mapper 的隐式值

Could not find implicit value for parameter Mapper

我尝试创建一个简单的基于无形的函数来将大小写 class 转换为字符串列表,然后我可以将其编码为 csv。

重点是在 case class 的每个成员上调用类型 class CsvEncoder,然后调用方法 encode 将其更改为字符串。

输入class:

trait CsvEncoder[T] {
  def encode(t: T): String
}

trait LowPriorityEncoder {
  implicit def genericEncoder[T]: CsvEncoder[T] = _.toString
}

object CsvEncoder extends LowPriorityEncoder {

  implicit def optionEncoder[T](
      implicit e: CsvEncoder[T]
  ): CsvEncoder[Option[T]] = _.fold("")(e.encode)

}

无形:

class CsvBuilder[Row](rows: List[Row]) {

  object csvMapper extends Poly1 {

    implicit def caseEncode[V](
        implicit e: CsvEncoder[V]
    ): Case.Aux[FieldType[Symbol, V], FieldType[Symbol, String]] =
      at[FieldType[Symbol, V]](s => field[Symbol](e.encode(s)))
  }

  def build[Repr <: HList, OutRepr <: HList](
      implicit labelledGeneric: LabelledGeneric.Aux[Row, Repr],
      mapper: Mapper.Aux[csvMapper.type, Repr, OutRepr],
      toMap: ToMap.Aux[OutRepr, Symbol, String]
  ): List[Map[String, String]] = {

    def transform(row: Row): Map[String, String] = {
      val formattedRows = labelledGeneric
        .to(row)
        .map(csvMapper)

      val fields = toMap(formattedRows)
        .map {
          case (k: Symbol, value: String) =>
            (k.toString -> value)
        }

      fields
    }

    rows.map(transform(_))

  }
}

当我尝试使用简单的大小写时 class:

final case class ProjectCsvRow(
    project: Option[String],
    something: Option[String],
    site: Option[String],
    state: Option[Int]
)

new CsvBuilder[ProjectCsvRow](
  List(ProjectCsvRow(Some(""), None, None, None))
).build

我得到

could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper.Aux[stabilizer.csvMapper.type,Repr,OutRepr]

我想我遗漏了什么,但我真的想不通。

我已经检查过是否每个字段都有 CsvEncoder 的实例。

首先,您的Poly不正确。输入 Symbol 太粗糙了。记录的键不仅仅是 Symbols,它们是 Symbol 的单例子类型。你应该定义

object csvMapper extends Poly1 {
  implicit def caseEncode[K <: Symbol, V](
    implicit e: CsvEncoder[V]
  ): Case.Aux[FieldType[K, V], FieldType[K, String]] =
    at[FieldType[K, V]](s => field[K](e.encode(s)))
}

其次,将 Poly 嵌套到 class(而不是对象)中可能很危险(它开始依赖于 CsvBuilder 的实例,因此依赖于类型 Row).所以把 csvMapper 移到 CsvBuilder 外面(这样它的路径就稳定了)。