我有一个大的平面非规范化csv文件,包含单行上的多个对象,如下所示:
a1, a2, a3, b1, b2, b3 ...
...
我有对象:
case class A(a1: Int, a2: String, a3: Float)
case class B...
...
并且遗留的是编写用于提取每个类的复杂适配器。我最近阅读了一些关于无形的讨论,我知道我可以用无形的通用编程解决这个问题。
甚至还有一个csv解析器示例,非常完美。我的想法是:
我还在使用scala 2.10,我似乎已经正确配置了编译器插件(egg.mvn clean install正常工作)。但intellij偶尔无法编译,抛出异常。
<groupId>org.scala-lang.plugins</groupId>
<artifactId>macro-paradise_2.10</artifactId>
<version>2.0.0-SNAPSHOT</version>
此代码来自shapeless
示例
implicit def deriveHConsOption[V, T <: HList](
implicit
scv: Lazy[CSVConverter[V]],
sct: Lazy[CSVConverter[T]]
):CSVConverter[Option[V] :: T] = new CSVConverter[Option[V] :: T] {
override def from(s: String): Try[shapeless.::[Option[V], T]] = s.span(_ != ',')
但是我遇到了以下编译错误:
错误:(70,28)::的类型参数错误,应为1
):CSVConverter [Option [V] :: T] = new CSVConverter [Option [V] :: T] { ^
我尝试使用无形过滤csv:
//code to filter and extract one object
def extractCSVColumnsAndParse[T]:
val labl = LabelledGeneric[T]
val keys = Keys[labl.Repr].apply
val keyNames = keys.toList.map(_.name)
错误:(86,35)找不到参数lgen的隐含值: shapeless.LabelledGeneric [T] val labl = LabelledGeneric [T]
答案 0 :(得分:1)
我对你的IntelliJ问题知之甚少,但我会给另外两个答案:
2:::
是scala.collection.immutable.::
(List
构造函数),除非您明确导入shapeless.::
(HList
构造函数)。
3:当你处理泛型类型时,你不能假设它们。它们可能是Int
,Any
,MyCaseClass
,Nothing
,...因此编译器无法为它们找到LabelledGeneric
(实际上,这将是什么LabelledGeneric
的{{1}}?)。因此,您必须明确告诉您的所有泛型方法您的类型具有Any
的实例,并且这是通过将其作为隐式参数(或上下文边界,在引擎盖下是相同的事物)来完成的。 。例如,你可以做到
LabelledGeneric[T]
然后,当您使用它时,对于显式案例类:
// alternatively, def extractCSVColumnsAndParse[T: LabelledGeneric]
def extractCSVColumnsAndParse[T](implicit ev: LabelledGeneric[T]) = {
val labl = LabelledGeneric[T]
val keys = Keys[labl.Repr].apply
val keyNames = keys.toList.map(_.name)
...
}
无形的“魔力”是它为你生成extractCSVColumnsAndParse[MyCaseClass] //no need to pass the parameter, it is already in scope
,但它只能为特定类型(使用宏)这样做,所以你必须告诉编译器它存在,如果你'处理泛型类型。
修改强>
之后,您收到implicit ev: LabelledGeneric[MyCaseClass]
错误,因为val keys
的类型参数必须是Keys
,因此您必须以某种方式强制执行此操作,因为{{1 }}不一定是HList
。而且,您还需要提供隐式LabelledGeneric[T]#Repr
,原因与HList
相同。
Keys[Repr]
但是,这使得使用特定案例类调用变得不那么容易,因为您不能再执行LabelledGeneric
。这是因为scala方法只有一个类型参数列表,所以你必须给它们全部或不给它们。
避免这种情况的一种复杂方法是以下模式,假设您的方法实际上有一些参数(例如,来自csv文件的def extractCSVColumnsAndParse[T, Repr <: HList](labl: LabelledGeneric.Aux[T, Repr], K: Keys[Repr]) {
val keys = K()
val keyNames = keys.toList.map(_.name)
...
}
或csv文件路径):
extractCSVColumnsAndParse[MyCaseClass]
现在您可以使用
进行调用List[String]
此模式允许您仅指定第一个类型参数,第二个参数在编译时推断。