对不起可怕的标题,不知道一个更好的。 这里是我的问题的一个粗略的简化(很抱歉,如果它看起来那么微不足道,这是毫无意义的):
class RList[T](data: List[T]) {
def map[V](f: T=>V): RList[V] = ...
}
一个的想法RList
(Restricted list中)是你不能调整其大小,或改变它的元素的顺序。 但是你可以使用,让您与它改变数据的新的rlist功能。
现在需要有创建RLists功能。 它可能有一个签名是这样的 :
def toRList[T](values: List[T]): RList[T] = ...
到现在为止还挺好。 但现在棘手的部分。 我需要的是这样的一个功能:
def zip[T, V](left: RList[T], right: RList[V]): RList[(T,V)]
但是,与另外的约束left
和right
具有相同的起源。 因此,它们保证是相同的大小。
例如,代码应该编译:
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(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)
}
}