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.

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

For reference, I am running scala 2.11.6 and shapeless 2.2.2

+3


source to share


1 answer


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).

+5


source







All Articles