简体   繁体   中英

Implicit Generic.Aux missing on conversion from Shapeless HList to case class

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.

https://github.com/milessabin/shapeless/wiki/Migration-guide:-shapeless-1.2.4-to-2.0.0#iso-is-now-generic

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM