我正在尝试为Scala集合编写一些扩展方法,并在完全生成它们的过程中遇到麻烦。
第一次尝试tailOption会产生类似以下内容:
implicit class TailOption[A, Repr <: GenTraversableLike[A, Repr]](val repr: Repr) {
def tailOption: Option[Repr] =
if (repr.isEmpty) None
else Some(repr.tail)
}
不幸的是,这不起作用:
scala> List(1,2,3).tailOption
<console>:19: error: value tailOption is not a member of List[Int]
List(1,2,3).tailOption
Scala 2.10提供了IsTraversableLike类型类,以帮助此类事情适应所有集合(包括诸如字符串之类的奇数)。
举例来说,我可以很容易地实现tailOption:
implicit class TailOption[Repr](val r: Repr)(implicit fr: IsTraversableLike[Repr]) {
def tailOption: Option[Repr] = {
val repr = fr.conversion(r)
if (repr.isEmpty) None
else Some(repr.tail)
}
}
scala> List(1,2,3).tailOption
res12: Option[List[Int]] = Some(List(2, 3))
scala> "one".tailOption
res13: Option[String] = Some(ne)
结果的类型正确:
Option[<input-type>]
。具体来说,当我调用返回Repr
的方法(如`tail)时,我已经能够保留Repr
类型。不幸的是,我似乎无法使用此技巧来保留集合元素的类型。我无法调用返回元素的方法。
IsTraversableLike
确实具有成员A,但它似乎不是很有用。特别是我无法重建原始元素类型,并且该成员的类型也不相同。例如,无需进一步工作,headTailOption
看起来像这样:implicit class HeadTailOption[Repr](val r: Repr)(implicit val fr: IsTraversableLike[Repr]) {
def headTailOption: Option[(fr.A, Repr)] = {
val repr = fr.conversion(r)
if (repr.isEmpty) None
else Some(repr.head -> repr.tail)
}
}
scala> val Some((c, _)) = "one".headTailOption
c: _1.fr.A forSome { val _1: HeadTailOption[String] } = o
如我们所见,c具有奇特的巴洛克风格。但是,此类型不等同于Char:
scala> val fr = implicitly[IsTraversableLike[String]]
fr: scala.collection.generic.IsTraversableLike[String] = scala.collection.generic.IsTraversableLike$$anon$1@60ab6a84
scala> implicitly[fr.A <:< Char]
<console>:25: error: Cannot prove that fr.A <:< Char.
implicitly[fr.A <:< Char]
我尝试了各种技巧,包括使用
Repr[A] <: GenTraversableLike[A, Repr[A]]
都无济于事。任何人都可以算出神奇的调味料,使headTailOption
返回以下正确的类型:val headTailString: Option[(Char, String)] = "one".headTailOption
val headTailList: Option[(Int, List[Int])] = List(1,2,3).headTailOption
最佳答案
部分答案。您可能已经从IsTraversableLike
的scaladoc示例开始。它仍然使用分离隐式转换并实例化包装器类的“旧方法”,而不是一步一步地通过隐式类。事实证明,“旧方法”确实有效:
import collection.GenTraversableLike
import collection.generic.IsTraversableLike
final class HeadTailOptionImpl[A, Repr](repr: GenTraversableLike[A, Repr]) {
def headTailOption: Option[(A, Repr)] = {
if (repr.isEmpty) None
else Some(repr.head -> repr.tail)
}
}
implicit def headTailOption[Repr](r: Repr)(implicit fr: IsTraversableLike[Repr]):
HeadTailOptionImpl[fr.A,Repr] = new HeadTailOptionImpl(fr.conversion(r))
// `c` looks still weird: `scala.collection.generic.IsTraversableLike.stringRepr.A`
val Some((c, _)) = "one".headTailOption
val d: Char = c // ...but it really is a `Char`!
val headTailString: Option[(Char, String)] = "one".headTailOption
val headTailList: Option[(Int, List[Int])] = List(1,2,3).headTailOption
正如Miles指出的那样,拆分对于使用隐式搜索和类型推断而言似乎是必不可少的。
另一个解决方案(尽管当然不那么优雅)是放弃字符串和集合的统一:
trait HeadTailOptionLike[A, Repr] {
def headTailOption: Option[(A, Repr)]
}
implicit class GenHeadTailOption[A, Repr](repr: GenTraversableLike[A, Repr])
extends HeadTailOptionLike[A, Repr] {
def headTailOption =
if (repr.isEmpty) None
else Some(repr.head -> repr.tail)
}
implicit class StringHeadTailOption(repr: String)
extends HeadTailOptionLike[Char, String] {
def headTailOption =
if (repr.isEmpty) None
else Some(repr.head -> repr.tail) // could use repr.charAt(0) -> repr.substring(1)
}
List(1,2,3).headTailOption
"one".headTailOption
关于scala - 如何在IsTraversableLike中使用类型成员A?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14180533/