I just recently started learning scala and today I decided I wanted to write a CSV parser that would load nicely into case classes but store the data in rows (lists) of Shapeless's HList object so that I could get some exposure to type-level programming.
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 that's loading the data set:
// 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
}
}
Things seem to be working alright until I added the get() with the conversion from the HList to the case class where I now get a compilation error. Why isn't the implicit getting loaded and what can I do to fix it or otherwise convert from the HList to the case class?
Error:(14, 17) could not find implicit value for parameter gen: shapeless.Generic.Aux[A,T]
return convert(rows(index))
^
I've been reading the shapeless documentation and it mentions that this area had been in flux between version 1 and 2, but I believe things should be working on my version of shapeless and scala so I suspect I've just done something incorrectly.
For reference, I'm running scala 2.11.6 and shapeless 2.2.2
You're very close. The problem is that Scala isn't going to propagate implicit requirements up the call chain automatically for you. If you need a Generic[A, T]
instance to call convert
, then you'll have to make sure that one's in scope every time you call convert convert
. If A
and T
are fixed (and are actually an case class- HList
pair), Shapeless will generate one for you. In your get
method, however, the compiler still knows nothing about A
and T
except that T
is an HList
, so you need to require the instance again in order to call convert
:
def get(index: Int)(implicit gen: Generic.Aux[A, T]): A = convert(rows(index))
Everything should work just fine after this change.
Note that you could also require the instance at the trait level by adding an (abstract) method like the following:
implicit def genA: Generic.Aux[A, T]
Then any class implementing LoadsCsv
could have an implicit val genA
parameter (or could supply the instance in some other way).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.