如何使用foldRight/foldLeft将HList转换为另一个HList

dav*_*rez 6 scala shapeless

这个问题来自我之前的问题:HList#foldLeft()返回什么?

我有这种情况:

class Cursor {
}

trait Column[T] {
   def read(c: Cursor, index: Int): T
}

object Columns {
    object readColumn extends Poly2 {
        implicit def a[A, B <: HList] = at[Column[A], (B, Cursor, Int)] { case (col, (values, cursor, index)) ?
            (col.read(cursor, index) :: values, cursor, index+1)
        }
    }

    def readColumns[A <: HList, B <: HList](c: Cursor, columns: A)(implicit l: RightFolder.Aux[A, (HNil.type, Cursor, Int), readColumn.type, (B, Cursor, Int)]): B =
        columnas.foldRight((HNil, c, 0))(readColumn)._1
}
Run Code Online (Sandbox Code Playgroud)

此代码尝试读取多个列的值.

如果我打电话readColumns(cursor, new Column[String] :: new Column[Int] :: HNil),我希望得到String :: Int :: HNil.

readColumns()方法编译好,但编译器抱怨具体调用中的含义.

什么是正确的工作方式?

更新1:

这是我用2列调用时收到的确切错误消息:

could not find implicit value for parameter l: 
shapeless.ops.hlist.RightFolder.Aux[shapeless.::[Column[String],shapeless.::
[Column[String],shapeless.HNil]],(shapeless.HNil.type, android.database.Cursor, Int),readColumn.type,(B, android.database.Cursor, Int)]
Run Code Online (Sandbox Code Playgroud)

不知道如何帮助编译器.:-(

更新2:

问题:为什么HNil.type在隐含参数中指定readColumns():RightFolder.Aux[A, (HNil.type, Cursor, Int), readColumn.type, (B, Cursor, Int)]

Tra*_*own 5

更新以解决编辑问题

这是一个完整的工作示例:

class Cursor {}

trait Column[T] {
   def read(c: Cursor, index: Int): T
}

import shapeless._, ops.hlist.RightFolder

object Columns {
  object readColumn extends Poly2 {
    implicit def a[A, B <: HList]: Case.Aux[
      Column[A],
      (B, Cursor, Int),
      (A :: B, Cursor, Int)
    ] = at[Column[A], (B, Cursor, Int)] {
      case (col, (values, cursor, index)) =>
        (col.read(cursor, index) :: values, cursor, index + 1)
    }
  }

  def readColumns[A <: HList, B <: HList](c: Cursor, columns: A)(implicit
    l: RightFolder.Aux[
      A,
      (HNil, Cursor, Int),
      readColumn.type,
      (B, Cursor, Int)
    ]
  ): B = columns.foldRight((HNil: HNil, c, 0))(readColumn)._1
}
Run Code Online (Sandbox Code Playgroud)

然后:

val stringColumn = new Column[String] {
  def read(c: Cursor, index: Int) = "foo"
}

val intColumn = new Column[Int] {
  def read(c: Cursor, index: Int) = 10
}

Columns.readColumns(new Cursor, stringColumn :: intColumn :: HNil)
Run Code Online (Sandbox Code Playgroud)

这编译得很好,并且在2.0.0和2.1.0-RC1上做了我期望的事情.

我应该在我原来的答复中提到,使用HNil.type这样的不理想,它工作得很好,但明确地键入HNil的参数foldRightHNil更好的解决方案.请注意,您必须执行其中一个,因为静态类型HNilHNil.type,并且RightFolder在其第二个参数中不是协变的.

原始答案

您的readColumn定义中存在一个非常小的错误- 您正在返回Tuple4,但您想要返回一个Tuple3.以下应该有效:

    object readColumn extends Poly2 {
       implicit def a[A, B <: HList]: Case.Aux[
         Column[A],
         (B, Cursor, Int),
         (A :: B, Cursor, Int)
       ] = at[Column[A], (B, Cursor, Int)] {
          case (col, (values, cursor, index)) =>
           (col.read(cursor, index) :: values, cursor, index+1)
       }
    }
Run Code Online (Sandbox Code Playgroud)

对于任何隐式方法提供显式返回类型通常是一个好主意,因为无关的原因与隐式解析有关,但在这种情况下明确返回类型也会很快发现错误.