scala 的解释 "double definition" fix using by-name params or DummyImplicit
Explanation of scala "double definition" fix using by-name params or DummyImplicit
我有几个解决“双重定义”问题的方法,但我无法理解他们真正在做什么来解决类型擦除问题。
我也会给出一些一般背景,因为我可能一开始就错误地解决了这个问题,但最终有助于理解 DummyImplicits 和在这种情况下的别名参数就足够了。
上下文
我正在为深层嵌套 JSON 替换解析器,其中几乎每个值都是可选的,并且几乎所有数据(包括 Int、Double 等)都存储为字符串。捕获解析值的 类 采用这种一般形式(目前)。
case class Field1(subfield1: Option[String], subfield2: Option[String]) {
def sf1Converted: Option[Int] =
Try { subfield1.get.toInt }.filter(i => lb <= i && i <= ub).toOption
def sf2Converted: Option[Double] =
Try { subfield2.get.toDouble }.filter(d => lb <= d && d <= ub).toOption
}
这个功能,但是......它非常慢并且需要不真实的内存量。我的猜测是将字符串直接转换为数字数据将大大减少内存,并可能以加载时间为代价加快数据的使用。
所以我想我会做类似...
/** type signature w/ numeric args instead of Strings */
case class Field1(subfield1: Option[Int], subfield2: Option[Double])
object Field1 {
/** the apply method I need for this class */
def apply(subfield1: Option[String], subfield2: Option[String]): Field1 =
new Field1(
Try { subfield1.get.toInt }.filter(i => lb <= i && i <= ub).toOption,
Try { subfield2.get.toDouble }.filter(d => lb <= d && d <= ub).toOption)
/** one more to generalize the question a bit more */
def apply(subfield1: Option[Int], subfield2: Option[Int]): Field1 =
new Field1(subfield1, Try { subfield2.get.toDouble }.filter(d => lb <= d && d <= ub).toOption)
}
这显然会引发一些 error: double definition ... have the same type after erasure
错误。所以我找到了两个解决方案......但我无法弄清楚其中一个实际上在做什么以及我可能会遇到一个或另一个的问题。
选项 1 在每个应用方法中创建一个(不同的)arg by-name param
object Field1 {
def apply(subfield1: => Option[String], subfield2: Option[String]): Field1 = ???
def apply(subfield1: Option[Int], subfield2: => Option[Int]): Field1 = ???
}
选项 2 虚拟隐式
object Field1 {
def apply(subfield1: Option[String], subfield2: Option[String])(implicit i: DummyImplicit): Field1 = ???
def apply(subfield1: Option[Int], subfield2: Option[Int])(implicit i: DummyImplicit, i2: DummyImplicit): Field1 = ???
}
问题
这两个都可以编译,但我不明白为什么其中一个可以解决类型擦除问题。
我认为按名称的参数只在使用时计算,并且每次使用时都计算。我不明白为什么这会修复类型擦除问题。
有一个很好的虚拟隐式演示 ,但是当它触及问题的核心时,它只是说 “每个调用都被赋予正确消除歧义所需的隐式参数电话。虚拟值如何做到这一点?
docs 只是说 “总是有隐式值的类型。”
可能是个糟糕的问题,因为我知道如何让它工作,但我完全不知道为什么这两个选项都有效。任何帮助表示赞赏。
option-1
的编译代码的相关部分看起来像
def apply(subfield1: Function0, subfield2: Option): Field1 = scala.Predef.???();
def apply(subfield1: Option, subfield2: Function0): Field1 = scala.Predef.???();
如您所见,名称参数已转换为 Function0
,因此可以重复使用该名称。
对于 option-2
,隐式柯里化参数作为额外参数添加到函数中,因此可以使用相同的名称。
def apply(subfield1: Option, subfield2: Option, i: DummyImplicit): Field1 = scala.Predef.???();
def apply(subfield1: Option, subfield2: Option, i: DummyImplicit, i2: DummyImplicit): Field1 = scala.Predef.???();
您还可以看到确实发生了类型擦除。
编辑 - 使用TypeTag
克服类型擦除
def apply[T: TypeTag](subfield1: => Option[T], subfield2: Option[T]): Field1 =
typeOf[T] match {
case tpe if tpe =:= typeOf[String] => new Field1(subfield1.asInstanceOf[Option[String]], subfield2.asInstanceOf[Option[String]])
case tpe if tpe =:= typeOf[Int] => ???
case _ => new Field1(subfield1.map(_.toString), subfield2.map(_.toString))
}
我有几个解决“双重定义”问题的方法,但我无法理解他们真正在做什么来解决类型擦除问题。
我也会给出一些一般背景,因为我可能一开始就错误地解决了这个问题,但最终有助于理解 DummyImplicits 和在这种情况下的别名参数就足够了。
上下文
我正在为深层嵌套 JSON 替换解析器,其中几乎每个值都是可选的,并且几乎所有数据(包括 Int、Double 等)都存储为字符串。捕获解析值的 类 采用这种一般形式(目前)。
case class Field1(subfield1: Option[String], subfield2: Option[String]) {
def sf1Converted: Option[Int] =
Try { subfield1.get.toInt }.filter(i => lb <= i && i <= ub).toOption
def sf2Converted: Option[Double] =
Try { subfield2.get.toDouble }.filter(d => lb <= d && d <= ub).toOption
}
这个功能,但是......它非常慢并且需要不真实的内存量。我的猜测是将字符串直接转换为数字数据将大大减少内存,并可能以加载时间为代价加快数据的使用。
所以我想我会做类似...
/** type signature w/ numeric args instead of Strings */
case class Field1(subfield1: Option[Int], subfield2: Option[Double])
object Field1 {
/** the apply method I need for this class */
def apply(subfield1: Option[String], subfield2: Option[String]): Field1 =
new Field1(
Try { subfield1.get.toInt }.filter(i => lb <= i && i <= ub).toOption,
Try { subfield2.get.toDouble }.filter(d => lb <= d && d <= ub).toOption)
/** one more to generalize the question a bit more */
def apply(subfield1: Option[Int], subfield2: Option[Int]): Field1 =
new Field1(subfield1, Try { subfield2.get.toDouble }.filter(d => lb <= d && d <= ub).toOption)
}
这显然会引发一些 error: double definition ... have the same type after erasure
错误。所以我找到了两个解决方案......但我无法弄清楚其中一个实际上在做什么以及我可能会遇到一个或另一个的问题。
选项 1 在每个应用方法中创建一个(不同的)arg by-name param
object Field1 {
def apply(subfield1: => Option[String], subfield2: Option[String]): Field1 = ???
def apply(subfield1: Option[Int], subfield2: => Option[Int]): Field1 = ???
}
选项 2 虚拟隐式
object Field1 {
def apply(subfield1: Option[String], subfield2: Option[String])(implicit i: DummyImplicit): Field1 = ???
def apply(subfield1: Option[Int], subfield2: Option[Int])(implicit i: DummyImplicit, i2: DummyImplicit): Field1 = ???
}
问题
这两个都可以编译,但我不明白为什么其中一个可以解决类型擦除问题。
我认为按名称的参数只在使用时计算,并且每次使用时都计算。我不明白为什么这会修复类型擦除问题。
有一个很好的虚拟隐式演示
docs 只是说 “总是有隐式值的类型。”
可能是个糟糕的问题,因为我知道如何让它工作,但我完全不知道为什么这两个选项都有效。任何帮助表示赞赏。
option-1
的编译代码的相关部分看起来像
def apply(subfield1: Function0, subfield2: Option): Field1 = scala.Predef.???();
def apply(subfield1: Option, subfield2: Function0): Field1 = scala.Predef.???();
如您所见,名称参数已转换为 Function0
,因此可以重复使用该名称。
对于 option-2
,隐式柯里化参数作为额外参数添加到函数中,因此可以使用相同的名称。
def apply(subfield1: Option, subfield2: Option, i: DummyImplicit): Field1 = scala.Predef.???();
def apply(subfield1: Option, subfield2: Option, i: DummyImplicit, i2: DummyImplicit): Field1 = scala.Predef.???();
您还可以看到确实发生了类型擦除。
编辑 - 使用TypeTag
克服类型擦除
def apply[T: TypeTag](subfield1: => Option[T], subfield2: Option[T]): Field1 =
typeOf[T] match {
case tpe if tpe =:= typeOf[String] => new Field1(subfield1.asInstanceOf[Option[String]], subfield2.asInstanceOf[Option[String]])
case tpe if tpe =:= typeOf[Int] => ???
case _ => new Field1(subfield1.map(_.toString), subfield2.map(_.toString))
}