Scala 泛型类型边界——指向实际类型
Scala Generics Type Bounds - Pointing to the Actual Type
我正在研究 scala 泛型和类型边界以了解其可能的用例。我对一个场景感到困惑。
假设我有一个特质 Combinable
trait Combinable[T] {
def combine(other: T): T
}
我想为 Vector[A] 实现一个隐式 def:
implicit def vectorCombinable[A](self: Vector[A]) = new Combinable[Vector[A]] { // note: using scala 2.11, no SAM support
override def combine(other: Vector[A]): Vector[A] = self ++ other
}
到目前为止一切都很好,如果我将 Vector 替换为 B 类型上限到 GenTraversable
:
,问题就开始了
implicit def vectorCombinable[A, B <: GenTraversable[A]](self: B): Combinable[B] = new Combinable[B] {
override def combine(other: B): B = self ++ other
}
我只是希望此方法在类型 B 中 return,但是 self ++ other
失败并出现以下编译错误:
Expression of type GenTraversable[A] doesn't conform to expected type
B
基本上,你不能这样做,因为 GenTraversable[A]
没有告诉你任何关于 ++
的 return 类型的具体信息,尤其是它不能保证你会return一个B
.
即使您扩展了 B <: GenTraversableLike[A, B]
,您仍然会遇到 ++
期望隐式 CanBuildFrom[Blah, Blahh, That]
而 return 是 That
的相同问题。
为了保证您的方法 combine
return 是同一类型的集合,而不依赖任何外部 CanBuildFrom
,您可以这样做:
import scala.collection._
import scala.collection.generic.GenericTraversableTemplate
import scala.language.implicitConversions
import scala.language.higherKinds
trait Combinable[T] {
def combine(other: T): T
}
implicit def genericCombinable
[A, CC[X] <:
GenericTraversableTemplate[X, CC] with
GenTraversable[X] with
TraversableOnce[X]
]
(c: CC[A])
: Combinable[CC[A]] = {
new Combinable[CC[A]] {
override def combine(other: CC[A]): CC[A] = {
val bldr = c.genericBuilder[A]
bldr ++= c
bldr ++= other
bldr.result
}
}
}
现在它可以编译并使用标准库中的大多数集合,因为它们中的大多数无论如何都倾向于实现 GenericTraversableTemplate
。
我建议你不要在这上面花太多时间。例如,scala cats 没有为所有可能的 GenTraversable
类型提供 Monoid
的通用实例,他们只是在 List
上实现了 Monoid
和 Vector
(以及其他一些 类),而不是 GenTraversable
(如果我错了请纠正我)。因此,我不会认为这是一件简单的事情。
最后一点:由于隐式转换,编译器应该给你警告,这是正确的。
你可以这样做:
implicit def vectorCombinable[A, B <: GenTraversableLike[A, B]]
(self: B with GenTraversable[A])
(implicit cbf: CanBuildFrom[B, A, B])
: Combinable[B] = new Combinable[B] {
override def combine(other: B): B = self ++ other
}
首先,您需要 B
来扩展 GenTraversableLike
,因为 scala.collection.???Like
类 在其签名中同时包含其元素的类型和序列的完整类型.例如 Vector[Int]
扩展 GenTraversableLike[Int, Vector[Int]]
。 ???Like
类 上定义的操作因此可以使用序列的完整类型。
其次,您需要 self
为 B with GenTraversable[A]
,因为编译器应该能够从单个签名中找出序列的类型及其元素的类型。
第三,您必须提供一个隐含的 CanBuildFrom[B, A, B]
,证明您可以使用序列 B
中类型 A
的元素构建序列 B
。此证明将提供给 GenTraversable
的 ++
方法
毕竟,它工作正常:
scala> List(1,2,3).combine(List(4,5,6))
res0: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> Set(1,2,3).combine(Set(4,5,6))
res1: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 3, 4)
scala> Map(1 -> "a", 2 -> "b").combine(Map(1 -> "c", 3 -> "d"))
res2: scala.collection.immutable.Map[Int,String] = Map(1 -> c, 2 -> b, 3 -> d)
我正在研究 scala 泛型和类型边界以了解其可能的用例。我对一个场景感到困惑。
假设我有一个特质 Combinable
trait Combinable[T] {
def combine(other: T): T
}
我想为 Vector[A] 实现一个隐式 def:
implicit def vectorCombinable[A](self: Vector[A]) = new Combinable[Vector[A]] { // note: using scala 2.11, no SAM support
override def combine(other: Vector[A]): Vector[A] = self ++ other
}
到目前为止一切都很好,如果我将 Vector 替换为 B 类型上限到 GenTraversable
:
implicit def vectorCombinable[A, B <: GenTraversable[A]](self: B): Combinable[B] = new Combinable[B] {
override def combine(other: B): B = self ++ other
}
我只是希望此方法在类型 B 中 return,但是 self ++ other
失败并出现以下编译错误:
Expression of type GenTraversable[A] doesn't conform to expected type B
基本上,你不能这样做,因为 GenTraversable[A]
没有告诉你任何关于 ++
的 return 类型的具体信息,尤其是它不能保证你会return一个B
.
即使您扩展了 B <: GenTraversableLike[A, B]
,您仍然会遇到 ++
期望隐式 CanBuildFrom[Blah, Blahh, That]
而 return 是 That
的相同问题。
为了保证您的方法 combine
return 是同一类型的集合,而不依赖任何外部 CanBuildFrom
,您可以这样做:
import scala.collection._
import scala.collection.generic.GenericTraversableTemplate
import scala.language.implicitConversions
import scala.language.higherKinds
trait Combinable[T] {
def combine(other: T): T
}
implicit def genericCombinable
[A, CC[X] <:
GenericTraversableTemplate[X, CC] with
GenTraversable[X] with
TraversableOnce[X]
]
(c: CC[A])
: Combinable[CC[A]] = {
new Combinable[CC[A]] {
override def combine(other: CC[A]): CC[A] = {
val bldr = c.genericBuilder[A]
bldr ++= c
bldr ++= other
bldr.result
}
}
}
现在它可以编译并使用标准库中的大多数集合,因为它们中的大多数无论如何都倾向于实现 GenericTraversableTemplate
。
我建议你不要在这上面花太多时间。例如,scala cats 没有为所有可能的 GenTraversable
类型提供 Monoid
的通用实例,他们只是在 List
上实现了 Monoid
和 Vector
(以及其他一些 类),而不是 GenTraversable
(如果我错了请纠正我)。因此,我不会认为这是一件简单的事情。
最后一点:由于隐式转换,编译器应该给你警告,这是正确的。
你可以这样做:
implicit def vectorCombinable[A, B <: GenTraversableLike[A, B]]
(self: B with GenTraversable[A])
(implicit cbf: CanBuildFrom[B, A, B])
: Combinable[B] = new Combinable[B] {
override def combine(other: B): B = self ++ other
}
首先,您需要 B
来扩展 GenTraversableLike
,因为 scala.collection.???Like
类 在其签名中同时包含其元素的类型和序列的完整类型.例如 Vector[Int]
扩展 GenTraversableLike[Int, Vector[Int]]
。 ???Like
类 上定义的操作因此可以使用序列的完整类型。
其次,您需要 self
为 B with GenTraversable[A]
,因为编译器应该能够从单个签名中找出序列的类型及其元素的类型。
第三,您必须提供一个隐含的 CanBuildFrom[B, A, B]
,证明您可以使用序列 B
中类型 A
的元素构建序列 B
。此证明将提供给 GenTraversable
++
方法
毕竟,它工作正常:
scala> List(1,2,3).combine(List(4,5,6))
res0: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> Set(1,2,3).combine(Set(4,5,6))
res1: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 3, 4)
scala> Map(1 -> "a", 2 -> "b").combine(Map(1 -> "c", 3 -> "d"))
res2: scala.collection.immutable.Map[Int,String] = Map(1 -> c, 2 -> b, 3 -> d)