Constrain function based on origin (Path Dependent type? Type Generation?)

时光怂恿深爱的人放手 提交于 2019-12-03 13:53:54

One of the disadvantages of making RList an inner trait of a producer is that it becomes less pleasant to write methods or functions with RList arguments outside of the producer—you end up with a lot of this:

def foo[P <: RListProducer, T](rl: P#RList[T]) = ???

An alternative is to give RList a type member that will be unique for each "source" RList, but that each source will pass on to its "children". Something like this:

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

Now assume we have the following:

val a = RList.toRList(1 :: 2 :: 3 :: Nil)
val b = a.map(_.toString)
val c = RList.toRList("1" :: "2" :: "3" :: Nil)

Now a zip b (or a zip b zip a zip a, etc.) will compile, but if you throw in a c you'll get a compiler error.


Note: I'd originally written zip as follows:

def zip[V](r: RList[V])(implicit ev: r.S =:= S) = new RList[(T, V)] { ... }

This gives a slightly nicer compiler error message, but if you're working pre-2.10 it requires turning on dependent method types with -Ydependent-method-types.

AlecZorab

Does this work for you?

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)
  }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!