Неявный Generic.Aux отсутствует при преобразовании из Shapeless HList в класс case

Я только недавно начал изучать scala, и сегодня я решил, что хочу написать парсер CSV, который будет хорошо загружаться в классы case, но хранить данные в строках (списках) объекта HList Shapeless, чтобы я мог немного познакомиться с программированием на уровне типов.

Вот что у меня есть на данный момент:

// 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

}

И объект, загружающий набор данных:

// 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
    }

}

Кажется, что все работает нормально, пока я не добавил get () с преобразованием из HList в класс case, где теперь я получаю ошибку компиляции. Почему неявная загрузка не загружается и что я могу сделать, чтобы исправить это или иным образом преобразовать из HList в класс case?

Error:(14, 17) could not find implicit value for parameter gen: shapeless.Generic.Aux[A,T]
        return convert(rows(index))
                      ^

Я читал документацию shapeless и там упоминается, что эта область находилась в постоянном движении между версиями 1 и 2, но я считаю, что следует работать над моей версией shapeless и scala, поэтому я подозреваю, что ' я только что сделал что-то неправильно.

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

Для справки, я запускаю scala 2.11.6 и shapeless 2.2.2


person RutledgePaulV    schedule 15.06.2015    source источник


Ответы (1)


Вы очень близки. Проблема в том, что Scala не собирается автоматически распространять неявные требования вверх по цепочке вызовов. Если вам нужен экземпляр Generic[A, T] для вызова convert, вам нужно будет убедиться, что он находится в области видимости каждый раз, когда вы вызываете convert convert. Если A и T зафиксированы (и на самом деле являются парой case class-HList), Shapeless сгенерирует его для вас. Однако в вашем get методе компилятор по-прежнему ничего не знает о A и T, за исключением того, что T является HList, поэтому вам нужно снова потребовать экземпляр, чтобы вызвать convert:

def get(index: Int)(implicit gen: Generic.Aux[A, T]): A = convert(rows(index))

После этого изменения все должно работать нормально.

Обратите внимание, что вам также может потребоваться экземпляр на уровне признака, добавив (абстрактный) метод, подобный следующему:

implicit def genA: Generic.Aux[A, T]

Тогда любой класс, реализующий LoadsCsv, может иметь неявный параметр val genA (или может предоставить экземпляр каким-либо другим способом).

person Travis Brown    schedule 15.06.2015
comment
Я слишком привык к тому, что компилятор scala творит чудеса :) Спасибо. - person RutledgePaulV; 15.06.2015