对于可怕的头衔感到抱歉,不确定是否更好。这是我的问题的一个简单的简化(对不起,如果它似乎微不足道,这是毫无意义的):
class RList[T](data: List[T]) {
def map[V](f: T=>V): RList[V] = ...
}
RList
(限制列表)的概念是您无法调整其大小,或更改其中元素的顺序。但是你可以使用函数为你提供一个包含更改数据的新RList。
现在需要有一个创建RList的函数。它可能有一个签名某些,如:
def toRList[T](values: List[T]): RList[T] = ...
到目前为止,这么好。但现在是棘手的部分。我需要一个像一样工作的函数::
def zip[T, V](left: RList[T], right: RList[V]): RList[(T,V)]
但是left
和right
具有相同来源的附加约束。因此,它们保证大小相同。
e.g。应编译的代码:
val x = toRList(List(1, 2, 3))
val y = x.map(_ * 2)
val z = y.map(_.toString)
zip(y,z)
e.g。应该编译
的代码val y = toRList(List(2, 4, 6))
val z = toRList(List("one", "two"))
zip(y,z)
* 注意:在我原来的问题中,zip的约束必须是来自同一个'source'。仅仅保证它们的长度相同还不够(更不用说,在编译时不知道列表的大小)*
我还需要能够多次使用zip
,所以这样的事情应该编译
zip(a,zip(b,c))
(假设a
,b
和c
来自同一来源)
谢谢!
答案 0 :(得分:4)
这对你有用吗?
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)
}
}
答案 1 :(得分:4)
使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
启用依赖方法类型。