我正在尝试为 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
在调用返回的方法时保留类型Repr
,比如`tail.
不幸的是,我似乎无法使用这个技巧来保留集合元素的类型。我不能调用返回元素的方法。
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