约束基于出身功能(路径依赖型?发电类型?)(Constrain function based on

2019-07-31 02:24发布

对不起可怕的标题,不知道一个更好的。 这里是我的问题的一个粗略的简化(很抱歉,如果它看起来那么微不足道,这是毫无意义的):

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

但是,与另外的约束leftright具有相同的起源。 因此,它们保证是相同的大小。

例如,代码应该编译:

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

(假设abc是来自相同的源)

谢谢!

Answer 1:

一个制作的缺点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



Answer 2:

这是否为您工作?

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


文章来源: Constrain function based on origin (Path Dependent type? Type Generation?)