基于原点约束函数(路径相关类型?类型生成?)



很抱歉标题太糟糕了,不确定有更好的标题。以下是我的问题的大致简化(很抱歉,如果它看起来很琐碎,那就毫无意义了):

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))

(假设abc来自同一来源)

谢谢!

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)
  }
}

最新更新