很抱歉标题太糟糕了,不确定有更好的标题。以下是我的问题的大致简化(很抱歉,如果它看起来很琐碎,那就毫无意义了):
class RList[T](data: List[T]) {
def map[V](f: T=>V): RList[V] = ...
}
RList
(Restricted List)的想法是,你不能调整它的大小,也不能改变其中元素的顺序。但你可以使用函数,为你提供一个新的RList,其中包含更改的数据。
现在需要一个创建RList的函数。它可能有一个签名类似于:
def toRList[T](values: List[T]): RList[T] = ...
到目前为止,一切都很好。但现在是棘手的部分。我需要一个功能,工作像这个:
def zip[T, V](left: RList[T], right: RList[V]): RList[(T,V)]
但在附加约束条件下,CCD_ 2和CCD_。因此,它们保证大小相同。
例如,应该编译的代码:
val x = toRList(List(1, 2, 3))
val y = x.map(_ * 2)
val z = y.map(_.toString)
zip(y,z)
例如,编译时应失败的代码
val y = toRList(List(2, 4, 6))
val z = toRList(List("one", "two"))
zip(y,z)
*注意:在我最初的问题中,zip的约束需要是它们来自同一个"源"。仅仅保证它们的长度相同是不够的(更不用说,在编译时还不知道列表的大小)*
我还需要能够多次使用zip
,所以像这样的东西应该编译
zip(a,zip(b,c))
(假设a
、b
和c
来自同一来源)
谢谢!
将RList
作为生产者的内部特征的缺点之一是,在生产者之外编写带有RList
参数的方法或函数会变得不那么愉快——最终会出现很多这样的情况:
def foo[P <: RListProducer, T](rl: P#RList[T]) = ???
另一种选择是给RList
一个类型成员,该成员对于每个"源"RList
都是唯一的,但每个源都将传递给其"子"。类似这样的东西:
trait RList[T] { outer =>
type S
protected val wrapped: List[T]
def map[V](f: T => V) = new RList[V] {
type S = outer.S
protected val wrapped = outer.wrapped.map(f)
}
def zip[V](r: RList[V] { type S = outer.S }) = new RList[(T, V)] {
type S = outer.S
protected val wrapped = outer.wrapped.zip(r.wrapped)
}
}
object RList {
def toRList[T](ts: List[T]) = new RList[T] {
type S = this.type
protected val wrapped = ts
}
}
现在假设我们有以下内容:
val a = RList.toRList(1 :: 2 :: 3 :: Nil)
val b = a.map(_.toString)
val c = RList.toRList("1" :: "2" :: "3" :: Nil)
现在a zip b
(或a zip b zip a zip a
等)将进行编译,但如果您抛出c
,则会出现编译器错误。
注意:我最初是这样写zip
的:
def zip[V](r: RList[V])(implicit ev: r.S =:= S) = new RList[(T, V)] { ... }
这提供了一个稍微好一点的编译器错误消息,但如果您使用2.10之前的版本,则需要使用-Ydependent-method-types
打开依赖方法类型。
这对你有用吗?
object PathDependentTypes {
trait RListProducer {
trait RList[T] {
def map[V](f: T => V): RList[V]
def zip[V](r: RList[V]) : RList[(T, V)]
}
def getList[T]: RList[T] = ???
}
class A extends RListProducer
def main(args: Array[String]) {
val aSource = new A
val a = aSource.getList[Int]
val anotherSource = new A
val b = anotherSource.getList[String]
val m1 = a.map( _ * 2)
val m2 = a.map( _.toString)
val z1 = m1.zip(m2)
//val z2 = a.zip(b) //doesn't compile because A and B aren't the same.
val z3 : A#RList[(Int, (Int, String))] = a zip (m1 zip m2)
}
}