This question is derived from my previous question: What does HList#foldLeft() return?
I have this scenario:
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
}
This code, tries to read the values of several columns.
If I call readColumns(cursor, new Column[String] :: new Column[Int] :: HNil)
, I expect to get String :: Int :: HNil
.
The readColumns()
method compiles ok, but the compiler complains about implicits in concrete invocations.
What is the right way of working?.
UPDATE 1:
Here is the exact error message I'm receiving when invoking with 2 columns:
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)]
Don't know how to help the compiler. :-(
UPDATE 2:
Question: why specify HNil.type
in the implicit parameter of readColumns()
: RightFolder.Aux[A, (HNil.type, Cursor, Int), readColumn.type, (B, Cursor, Int)]
?
Here's a complete working example:
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
}
And then:
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)
This compiles just fine and does what I'd expect on both 2.0.0 and 2.1.0-RC1.
And I should have mentioned in my original answer that using HNil.type
like that isn't ideal—it works just fine, but explicitly typing the HNil
in the argument to foldRight
as HNil
is a better solution. Note that you have to do one or the other, since the static type of HNil
is HNil.type
, and RightFolder
isn't covariant in its second argument.
There's a very minor error in your readColumn
definition—you're returning a Tuple4
, but you want to return a Tuple3
. The following should work:
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)
}
}
It's usually a good idea to provide an explicit return type for any implicit method for unrelated reasons having to do with implicit resolution, but in this case being explicit about the return type also turns up the error pretty quickly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With