Implicit Generic.Aux missing when converting from Shapeless HList to case class
I recently started learning scala and today I figured I want to write a CSV parser that will load well into case classes but store data in strings (lists) of a Shapeless HList object so that I can get some level programming done.
Here's what I have so far:
// LoadsCsv.scala
import shapeless._
import scala.collection.mutable
trait LoadsCsv[A, T <: HList] {
val rows: mutable.MutableList[T] = new mutable.MutableList[T]
def convert(t: T)(implicit gen: Generic.Aux[A, T]): A = gen.from(t)
def get(index: Int): A = {
convert(rows(index))
}
def load(file: String): Unit = {
val lines = io.Source.fromFile(file).getLines()
lines.foreach(line => rows += parse(line.split(",")))
}
def parse(line: Array[String]): T
}
And the object loading the dataset:
// TennisData.scala
import shapeless._
case class TennisData(weather:String, low:Int, high:Int, windy:Boolean, play:Boolean)
object TennisData extends LoadsCsv[TennisData, String :: Int :: Int :: Boolean :: Boolean :: HNil] {
load("tennis.csv")
override def parse(line: Array[String]) = {
line(0) :: line(1).toInt :: line(2).toInt :: line(3).toBoolean :: line(4).toBoolean :: HNil
}
}
Everything seems to work fine until I added a get () converting from an HList to a case class where I now get a compile error. Why isn't loading implied and what can I do to fix it or otherwise convert from HList to case class?
Error:(14, 17) could not find implicit value for parameter gen: shapeless.Generic.Aux[A,T]
return convert(rows(index))
^
I was reading the shapeless documentation and it mentions that this area was in flux between versions 1 and 2, but I believe everything should work on my version of shapeless and scala, so I suspect I'm just doing something wrong.
For reference, I am running scala 2.11.6 and shapeless 2.2.2
source to share
You are very close. The problem is, Scala won't automatically propagate implicit requirements down the call chain for you. If you need an instance Generic[A, T]
to invoke convert
then you will need to make sure everyone is in scope every time you call convert convert
. If A
u T
are fixed (and are actually a case- pair HList
), Shapeless will generate one for you. However, in your method, the get
compiler knows nothing about A
and T
other than what T
is HList
, so you need to require the instance again to call convert
:
def get(index: Int)(implicit gen: Generic.Aux[A, T]): A = convert(rows(index))
After this change, everything should work fine.
Note that you can also require an instance at the trait level by adding an (abstract) method as shown below:
implicit def genA: Generic.Aux[A, T]
Then any class that implements LoadsCsv
can have an implicit parameter val genA
(or can provide an instance in some other way).
source to share